mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Use unified StickersListFooter for emoji / stickers.
This commit is contained in:
parent
25f4646cd8
commit
d3f62d971d
8 changed files with 1292 additions and 1029 deletions
|
@ -370,6 +370,8 @@ PRIVATE
|
||||||
chat_helpers/stickers_emoji_pack.h
|
chat_helpers/stickers_emoji_pack.h
|
||||||
chat_helpers/stickers_dice_pack.cpp
|
chat_helpers/stickers_dice_pack.cpp
|
||||||
chat_helpers/stickers_dice_pack.h
|
chat_helpers/stickers_dice_pack.h
|
||||||
|
chat_helpers/stickers_list_footer.cpp
|
||||||
|
chat_helpers/stickers_list_footer.h
|
||||||
chat_helpers/stickers_list_widget.cpp
|
chat_helpers/stickers_list_widget.cpp
|
||||||
chat_helpers/stickers_list_widget.h
|
chat_helpers/stickers_list_widget.h
|
||||||
chat_helpers/stickers_lottie.cpp
|
chat_helpers/stickers_lottie.cpp
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "chat_helpers/stickers_list_widget.h"
|
#include "chat_helpers/stickers_list_widget.h"
|
||||||
|
#include "chat_helpers/stickers_list_footer.h"
|
||||||
#include "emoji_suggestions_data.h"
|
#include "emoji_suggestions_data.h"
|
||||||
#include "emoji_suggestions_helper.h"
|
#include "emoji_suggestions_helper.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -85,6 +86,7 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
class EmojiListWidget::Footer : public TabbedSelector::InnerFooter {
|
class EmojiListWidget::Footer : public TabbedSelector::InnerFooter {
|
||||||
public:
|
public:
|
||||||
Footer(not_null<EmojiListWidget*> parent);
|
Footer(not_null<EmojiListWidget*> parent);
|
||||||
|
@ -102,6 +104,7 @@ private:
|
||||||
std::array<object_ptr<Ui::IconButton>, kEmojiSectionCount> _sections;
|
std::array<object_ptr<Ui::IconButton>, kEmojiSectionCount> _sections;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct EmojiListWidget::CustomInstance {
|
struct EmojiListWidget::CustomInstance {
|
||||||
CustomInstance(
|
CustomInstance(
|
||||||
|
@ -127,6 +130,7 @@ EmojiListWidget::CustomInstance::CustomInstance(
|
||||||
, object(&emoji, std::move(repaint)) {
|
, object(&emoji, std::move(repaint)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
EmojiListWidget::Footer::Footer(not_null<EmojiListWidget*> parent)
|
EmojiListWidget::Footer::Footer(not_null<EmojiListWidget*> parent)
|
||||||
: InnerFooter(parent)
|
: InnerFooter(parent)
|
||||||
, _pan(parent)
|
, _pan(parent)
|
||||||
|
@ -185,6 +189,7 @@ void EmojiListWidget::Footer::setCurrentSectionIcon(Section section) {
|
||||||
void EmojiListWidget::Footer::setActiveSection(Ui::Emoji::Section section) {
|
void EmojiListWidget::Footer::setActiveSection(Ui::Emoji::Section section) {
|
||||||
_pan->showEmojiSection(section);
|
_pan->showEmojiSection(section);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
EmojiColorPicker::EmojiColorPicker(QWidget *parent)
|
EmojiColorPicker::EmojiColorPicker(QWidget *parent)
|
||||||
: RpWidget(parent) {
|
: RpWidget(parent) {
|
||||||
|
@ -552,7 +557,9 @@ void EmojiListWidget::visibleTopBottomUpdated(
|
||||||
int visibleBottom) {
|
int visibleBottom) {
|
||||||
Inner::visibleTopBottomUpdated(visibleTop, visibleBottom);
|
Inner::visibleTopBottomUpdated(visibleTop, visibleBottom);
|
||||||
if (_footer) {
|
if (_footer) {
|
||||||
_footer->setCurrentSectionIcon(currentSection(visibleTop));
|
_footer->validateSelectedIcon(
|
||||||
|
currentSet(visibleTop),
|
||||||
|
ValidateIconAnimations::Full);
|
||||||
}
|
}
|
||||||
unloadNotSeenCustom(visibleTop, visibleBottom);
|
unloadNotSeenCustom(visibleTop, visibleBottom);
|
||||||
}
|
}
|
||||||
|
@ -580,8 +587,19 @@ void EmojiListWidget::unloadNotSeenCustom(
|
||||||
|
|
||||||
object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
|
object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
|
||||||
Expects(_footer == nullptr);
|
Expects(_footer == nullptr);
|
||||||
auto result = object_ptr<Footer>(this);
|
|
||||||
|
using FooterDescriptor = StickersListFooter::Descriptor;
|
||||||
|
auto result = object_ptr<StickersListFooter>(FooterDescriptor{
|
||||||
|
.controller = controller(),
|
||||||
|
.parent = this,
|
||||||
|
});
|
||||||
_footer = result;
|
_footer = result;
|
||||||
|
|
||||||
|
_footer->setChosen(
|
||||||
|
) | rpl::start_with_next([=](uint64 setId) {
|
||||||
|
showSet(setId);
|
||||||
|
}, _footer->lifetime());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +683,17 @@ int EmojiListWidget::countDesiredHeight(int newWidth) {
|
||||||
_rowsLeft -= st::roundRadiusSmall;
|
_rowsLeft -= st::roundRadiusSmall;
|
||||||
_singleSize = QSize(singleWidth, singleWidth - 4 * st::lineWidth);
|
_singleSize = QSize(singleWidth, singleWidth - 4 * st::lineWidth);
|
||||||
_picker->setSingleSize(_singleSize);
|
_picker->setSingleSize(_singleSize);
|
||||||
return sectionInfo(sectionsCount() - 1).rowsBottom + st::emojiPanPadding;
|
|
||||||
|
auto visibleHeight = minimalHeight();
|
||||||
|
auto minimalHeight = (visibleHeight - st::stickerPanPadding);
|
||||||
|
auto countResult = [this](int minimalLastHeight) {
|
||||||
|
const auto info = sectionInfo(sectionsCount() - 1);
|
||||||
|
return info.top
|
||||||
|
+ qMax(info.rowsBottom - info.top, minimalLastHeight);
|
||||||
|
};
|
||||||
|
const auto minimalLastHeight = minimalHeight;
|
||||||
|
return qMax(minimalHeight, countResult(minimalLastHeight))
|
||||||
|
+ st::emojiPanPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::ensureLoaded(int section) {
|
void EmojiListWidget::ensureLoaded(int section) {
|
||||||
|
@ -1052,8 +1080,8 @@ void EmojiListWidget::clearSelection() {
|
||||||
_lastMousePos = mapToGlobal(QPoint(-10, -10));
|
_lastMousePos = mapToGlobal(QPoint(-10, -10));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ui::Emoji::Section EmojiListWidget::currentSection(int yOffset) const {
|
uint64 EmojiListWidget::currentSet(int yOffset) const {
|
||||||
return static_cast<Section>(sectionInfoByOffset(yOffset).section);
|
return sectionSetId(sectionInfoByOffset(yOffset).section);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EmojiListWidget::tooltipText() const {
|
QString EmojiListWidget::tooltipText() const {
|
||||||
|
@ -1132,7 +1160,7 @@ void EmojiListWidget::refreshCustom() {
|
||||||
return true;
|
return true;
|
||||||
}();
|
}();
|
||||||
if (valid) {
|
if (valid) {
|
||||||
_custom.push_back(base::take(*i));
|
_custom.push_back(std::move(*i));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1170,10 +1198,51 @@ void EmojiListWidget::refreshCustom() {
|
||||||
}
|
}
|
||||||
_custom.push_back({
|
_custom.push_back({
|
||||||
.id = setId,
|
.id = setId,
|
||||||
|
.set = it->second.get(),
|
||||||
.title = it->second->title,
|
.title = it->second->title,
|
||||||
.list = std::move(set),
|
.list = std::move(set),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
_footer->refreshIcons(
|
||||||
|
fillIcons(),
|
||||||
|
nullptr,
|
||||||
|
ValidateIconAnimations::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<StickerIcon> EmojiListWidget::fillIcons() {
|
||||||
|
auto result = std::vector<StickerIcon>();
|
||||||
|
result.reserve(kEmojiSectionCount + _custom.size());
|
||||||
|
|
||||||
|
for (auto i = 0; i != kEmojiSectionCount; ++i) {
|
||||||
|
result.emplace_back(EmojiSectionSetId(static_cast<Section>(i)));
|
||||||
|
}
|
||||||
|
for (const auto &custom : _custom) {
|
||||||
|
const auto set = custom.set;
|
||||||
|
const auto s = custom.list[0].document;
|
||||||
|
const auto availw = st::stickerIconWidth - 2 * st::stickerIconPadding;
|
||||||
|
const auto availh = st::emojiFooterHeight - 2 * st::stickerIconPadding;
|
||||||
|
const auto size = set->hasThumbnail()
|
||||||
|
? QSize(
|
||||||
|
set->thumbnailLocation().width(),
|
||||||
|
set->thumbnailLocation().height())
|
||||||
|
: s->hasThumbnail()
|
||||||
|
? QSize(
|
||||||
|
s->thumbnailLocation().width(),
|
||||||
|
s->thumbnailLocation().height())
|
||||||
|
: QSize();
|
||||||
|
auto thumbw = size.width(), thumbh = size.height(), pixw = 1, pixh = 1;
|
||||||
|
if (availw * thumbh > availh * thumbw) {
|
||||||
|
pixh = availh;
|
||||||
|
pixw = (pixh * thumbw) / thumbh;
|
||||||
|
} else {
|
||||||
|
pixw = availw;
|
||||||
|
pixh = thumbw ? ((pixw * thumbh) / thumbw) : 1;
|
||||||
|
}
|
||||||
|
if (pixw < 1) pixw = 1;
|
||||||
|
if (pixh < 1) pixh = 1;
|
||||||
|
result.emplace_back(set, s, pixw, pixh);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmojiListWidget::eventHook(QEvent *e) {
|
bool EmojiListWidget::eventHook(QEvent *e) {
|
||||||
|
@ -1293,13 +1362,17 @@ QPoint EmojiListWidget::buttonRippleTopLeft(int section) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::showEmojiSection(Section section) {
|
void EmojiListWidget::showEmojiSection(Section section) {
|
||||||
|
showSet(EmojiSectionSetId(section));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiListWidget::showSet(uint64 setId) {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
|
||||||
refreshRecent();
|
refreshRecent();
|
||||||
|
|
||||||
auto y = 0;
|
auto y = 0;
|
||||||
enumerateSections([&](const SectionInfo &info) {
|
enumerateSections([&](const SectionInfo &info) {
|
||||||
if (static_cast<Section>(info.section) == section) {
|
if (setId == sectionSetId(info.section)) {
|
||||||
y = info.top;
|
y = info.top;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1312,26 +1385,14 @@ void EmojiListWidget::showEmojiSection(Section section) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::showCustomSet(uint64 setId) {
|
uint64 EmojiListWidget::sectionSetId(int section) const {
|
||||||
clearSelection();
|
Expects(section < kEmojiSectionCount
|
||||||
|
|| (section - kEmojiSectionCount) < _custom.size());
|
||||||
|
|
||||||
refreshCustom();
|
return (section < kEmojiSectionCount)
|
||||||
|
? EmojiSectionSetId(static_cast<Section>(section))
|
||||||
|
: _custom[section - kEmojiSectionCount].id;
|
||||||
|
|
||||||
auto y = 0;
|
|
||||||
enumerateSections([&](const SectionInfo &info) {
|
|
||||||
if (info.section >= kEmojiSectionCount) {
|
|
||||||
if (_custom[info.section - kEmojiSectionCount].id == setId) {
|
|
||||||
y = info.top;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
scrollTo(y);
|
|
||||||
|
|
||||||
_lastMousePos = QCursor::pos();
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tr::phrase<> EmojiCategoryTitle(int index) {
|
tr::phrase<> EmojiCategoryTitle(int index) {
|
||||||
|
|
|
@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/tooltip.h"
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class StickersSet;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace tr {
|
namespace tr {
|
||||||
template <typename ...Tags>
|
template <typename ...Tags>
|
||||||
struct phrase;
|
struct phrase;
|
||||||
|
@ -37,7 +41,9 @@ namespace ChatHelpers {
|
||||||
|
|
||||||
inline constexpr auto kEmojiSectionCount = 8;
|
inline constexpr auto kEmojiSectionCount = 8;
|
||||||
|
|
||||||
|
struct StickerIcon;
|
||||||
class EmojiColorPicker;
|
class EmojiColorPicker;
|
||||||
|
class StickersListFooter;
|
||||||
|
|
||||||
class EmojiListWidget
|
class EmojiListWidget
|
||||||
: public TabbedSelector::Inner
|
: public TabbedSelector::Inner
|
||||||
|
@ -55,9 +61,8 @@ public:
|
||||||
object_ptr<TabbedSelector::InnerFooter> createFooter() override;
|
object_ptr<TabbedSelector::InnerFooter> createFooter() override;
|
||||||
|
|
||||||
void showEmojiSection(Section section);
|
void showEmojiSection(Section section);
|
||||||
[[nodiscard]] Section currentSection(int yOffset) const;
|
void showSet(uint64 setId);
|
||||||
|
[[nodiscard]] uint64 currentSet(int yOffset) const;
|
||||||
void showCustomSet(uint64 setId);
|
|
||||||
|
|
||||||
// Ui::AbstractTooltipShower interface.
|
// Ui::AbstractTooltipShower interface.
|
||||||
QString tooltipText() const override;
|
QString tooltipText() const override;
|
||||||
|
@ -87,8 +92,6 @@ protected:
|
||||||
int countDesiredHeight(int newWidth) override;
|
int countDesiredHeight(int newWidth) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Footer;
|
|
||||||
|
|
||||||
struct SectionInfo {
|
struct SectionInfo {
|
||||||
int section = 0;
|
int section = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -104,6 +107,7 @@ private:
|
||||||
};
|
};
|
||||||
struct CustomSet {
|
struct CustomSet {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
|
not_null<Data::StickersSet*> set;
|
||||||
QString title;
|
QString title;
|
||||||
std::vector<CustomOne> list;
|
std::vector<CustomOne> list;
|
||||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||||
|
@ -188,6 +192,8 @@ private:
|
||||||
[[nodiscard]] QRect emojiRect(int section, int index) const;
|
[[nodiscard]] QRect emojiRect(int section, int index) const;
|
||||||
[[nodiscard]] int emojiRight() const;
|
[[nodiscard]] int emojiRight() const;
|
||||||
[[nodiscard]] int emojiLeft() const;
|
[[nodiscard]] int emojiLeft() const;
|
||||||
|
[[nodiscard]] uint64 sectionSetId(int section) const;
|
||||||
|
[[nodiscard]] std::vector<StickerIcon> fillIcons();
|
||||||
|
|
||||||
void displaySet(uint64 setId);
|
void displaySet(uint64 setId);
|
||||||
void removeSet(uint64 setId);
|
void removeSet(uint64 setId);
|
||||||
|
@ -204,7 +210,7 @@ private:
|
||||||
void scheduleRepaintTimer();
|
void scheduleRepaintTimer();
|
||||||
void invokeRepaints();
|
void invokeRepaints();
|
||||||
|
|
||||||
Footer *_footer = nullptr;
|
StickersListFooter *_footer = nullptr;
|
||||||
|
|
||||||
int _counts[kEmojiSectionCount];
|
int _counts[kEmojiSectionCount];
|
||||||
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
|
QVector<EmojiPtr> _emoji[kEmojiSectionCount];
|
||||||
|
|
936
Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
Normal file
936
Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
Normal file
|
@ -0,0 +1,936 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "chat_helpers/stickers_list_footer.h"
|
||||||
|
|
||||||
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
|
#include "data/stickers/data_stickers_set.h"
|
||||||
|
#include "data/stickers/data_stickers.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_document_media.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "lottie/lottie_single_player.h"
|
||||||
|
#include "ui/widgets/input_fields.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
namespace ChatHelpers {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kEmojiSectionSetIdBase = uint64(0x77FF'FFFF'FFFF'FFF0ULL);
|
||||||
|
|
||||||
|
using EmojiSection = Ui::Emoji::Section;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
uint64 EmojiSectionSetId(EmojiSection section) {
|
||||||
|
Expects(section >= EmojiSection::Recent
|
||||||
|
&& section <= EmojiSection::Symbols);
|
||||||
|
|
||||||
|
return kEmojiSectionSetIdBase + static_cast<uint64>(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<EmojiSection> SetIdEmojiSection(uint64 id) {
|
||||||
|
const auto base = EmojiSectionSetId(EmojiSection::Recent);
|
||||||
|
if (id < base) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto index = id - base;
|
||||||
|
return (index <= uint64(EmojiSection::Symbols))
|
||||||
|
? static_cast<EmojiSection>(index)
|
||||||
|
: std::optional<EmojiSection>();
|
||||||
|
}
|
||||||
|
|
||||||
|
StickerIcon::StickerIcon(uint64 setId) : setId(setId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
StickerIcon::StickerIcon(
|
||||||
|
not_null<Data::StickersSet*> set,
|
||||||
|
DocumentData *sticker,
|
||||||
|
int pixw,
|
||||||
|
int pixh)
|
||||||
|
: setId(set->id)
|
||||||
|
, set(set)
|
||||||
|
, sticker(sticker)
|
||||||
|
, pixw(pixw)
|
||||||
|
, pixh(pixh) {
|
||||||
|
}
|
||||||
|
|
||||||
|
StickerIcon::StickerIcon(StickerIcon&&) = default;
|
||||||
|
|
||||||
|
StickerIcon &StickerIcon::operator=(StickerIcon&&) = default;
|
||||||
|
|
||||||
|
StickerIcon::~StickerIcon() = default;
|
||||||
|
|
||||||
|
void StickerIcon::ensureMediaCreated() const {
|
||||||
|
if (!sticker) {
|
||||||
|
return;
|
||||||
|
} else if (set->hasThumbnail()) {
|
||||||
|
if (!thumbnailMedia) {
|
||||||
|
thumbnailMedia = set->createThumbnailView();
|
||||||
|
set->loadThumbnail();
|
||||||
|
}
|
||||||
|
} else if (!stickerMedia) {
|
||||||
|
stickerMedia = sticker->createMediaView();
|
||||||
|
stickerMedia->thumbnailWanted(sticker->stickerSetOrigin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StickersListFooter::StickersListFooter(Descriptor &&descriptor)
|
||||||
|
: InnerFooter(descriptor.parent)
|
||||||
|
, _controller(descriptor.controller)
|
||||||
|
, _searchButtonVisible(descriptor.searchButtonVisible)
|
||||||
|
, _settingsButtonVisible(descriptor.settingsButtonVisible)
|
||||||
|
, _iconsAnimation([=](crl::time now) {
|
||||||
|
return iconsAnimationCallback(now);
|
||||||
|
}) {
|
||||||
|
setMouseTracking(true);
|
||||||
|
|
||||||
|
_iconsLeft = st::emojiCategorySkip + (_searchButtonVisible
|
||||||
|
? st::stickerIconWidth
|
||||||
|
: 0);
|
||||||
|
_iconsRight = st::emojiCategorySkip + (_settingsButtonVisible
|
||||||
|
? st::stickerIconWidth
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
_controller->session().downloaderTaskFinished(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
style::PaletteChanged(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_premiumIcon = QImage();
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::validatePremiumIcon() const {
|
||||||
|
if (!_premiumIcon.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto size = st::stickersPremium.size();
|
||||||
|
const auto mask = st::stickersPremium.instance(Qt::white);
|
||||||
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
_premiumIcon = QImage(
|
||||||
|
size * factor,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_premiumIcon.setDevicePixelRatio(factor);
|
||||||
|
|
||||||
|
QPainter p(&_premiumIcon);
|
||||||
|
auto gradient = QLinearGradient(
|
||||||
|
QPoint(0, size.height()),
|
||||||
|
QPoint(size.width(), 0));
|
||||||
|
gradient.setStops({
|
||||||
|
{ 0., st::stickerPanPremium1->c },
|
||||||
|
{ 1., st::stickerPanPremium2->c },
|
||||||
|
});
|
||||||
|
p.fillRect(QRect(QPoint(), size), gradient);
|
||||||
|
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||||
|
p.drawImage(QRect(QPoint(), size), mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::clearHeavyData() {
|
||||||
|
const auto count = int(_icons.size());
|
||||||
|
const auto iconsX = qRound(_iconsX.current());
|
||||||
|
enumerateIcons([&](const IconInfo &info) {
|
||||||
|
auto &icon = _icons[info.index];
|
||||||
|
icon.webm = nullptr;
|
||||||
|
icon.lottie = nullptr;
|
||||||
|
icon.lifetime.destroy();
|
||||||
|
icon.stickerMedia = nullptr;
|
||||||
|
if (!info.visible) {
|
||||||
|
icon.savedFrame = QPixmap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::initSearch() {
|
||||||
|
_searchField.create(
|
||||||
|
this,
|
||||||
|
st::gifsSearchField,
|
||||||
|
tr::lng_stickers_search_sets());
|
||||||
|
_searchCancel.create(this, st::gifsSearchCancel);
|
||||||
|
_searchField->show();
|
||||||
|
_searchCancel->show(anim::type::instant);
|
||||||
|
|
||||||
|
const auto cancelSearch = [=] {
|
||||||
|
if (_searchField->getLastText().isEmpty()) {
|
||||||
|
toggleSearch(false);
|
||||||
|
} else {
|
||||||
|
_searchField->setText(QString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
connect(_searchField, &Ui::InputField::submitted, [=] {
|
||||||
|
_searchRequests.fire({
|
||||||
|
.text = _searchField->getLastText(),
|
||||||
|
.forced = true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
connect(_searchField, &Ui::InputField::cancelled, cancelSearch);
|
||||||
|
connect(_searchField, &Ui::InputField::changed, [=] {
|
||||||
|
_searchRequests.fire({
|
||||||
|
.text = _searchField->getLastText(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_searchCancel->setClickedCallback(cancelSearch);
|
||||||
|
|
||||||
|
resizeSearchControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::toggleSearch(bool visible) {
|
||||||
|
if (_searchShown == visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_searchShown = visible;
|
||||||
|
if (_searchShown) {
|
||||||
|
initSearch();
|
||||||
|
stealFocus();
|
||||||
|
} else if (_searchField) {
|
||||||
|
returnFocus();
|
||||||
|
_searchField.destroy();
|
||||||
|
_searchCancel.destroy();
|
||||||
|
_focusTakenFrom = nullptr;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::stealFocus() {
|
||||||
|
if (_searchField) {
|
||||||
|
if (!_focusTakenFrom) {
|
||||||
|
_focusTakenFrom = QApplication::focusWidget();
|
||||||
|
}
|
||||||
|
_searchField->setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::returnFocus() {
|
||||||
|
if (_searchField && _focusTakenFrom) {
|
||||||
|
if (_searchField->hasFocus()) {
|
||||||
|
_focusTakenFrom->setFocus();
|
||||||
|
}
|
||||||
|
_focusTakenFrom = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::enumerateVisibleIcons(
|
||||||
|
Fn<void(const IconInfo &)> callback) {
|
||||||
|
auto iconsX = qRound(_iconsX.current());
|
||||||
|
auto x = _iconsLeft - (iconsX % st::stickerIconWidth);
|
||||||
|
auto first = floorclamp(iconsX, st::stickerIconWidth, 0, _icons.size());
|
||||||
|
auto last = ceilclamp(
|
||||||
|
iconsX + width(),
|
||||||
|
st::stickerIconWidth,
|
||||||
|
0,
|
||||||
|
_icons.size());
|
||||||
|
for (auto index = first; index != last; ++index) {
|
||||||
|
callback({ .index = index, .left = x, .visible = true });
|
||||||
|
x += st::stickerIconWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::enumerateIcons(
|
||||||
|
Fn<void(const IconInfo &)> callback) {
|
||||||
|
auto iconsX = qRound(_iconsX.current());
|
||||||
|
auto x = _iconsLeft - (iconsX % st::stickerIconWidth);
|
||||||
|
auto first = floorclamp(iconsX, st::stickerIconWidth, 0, _icons.size());
|
||||||
|
auto last = ceilclamp(
|
||||||
|
iconsX + width(),
|
||||||
|
st::stickerIconWidth,
|
||||||
|
0,
|
||||||
|
_icons.size());
|
||||||
|
x -= first * st::stickerIconWidth;
|
||||||
|
for (auto i = 0, count = int(_icons.size()); i != count; ++i) {
|
||||||
|
const auto visible = (i >= first && i < last);
|
||||||
|
callback({ .index = i, .left = x, .visible = visible });
|
||||||
|
x += st::stickerIconWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::preloadImages() {
|
||||||
|
enumerateVisibleIcons([&](const IconInfo &info) {
|
||||||
|
const auto &icon = _icons[info.index];
|
||||||
|
if (const auto sticker = icon.sticker) {
|
||||||
|
Assert(icon.set != nullptr);
|
||||||
|
if (icon.set->hasThumbnail()) {
|
||||||
|
icon.set->loadThumbnail();
|
||||||
|
} else {
|
||||||
|
sticker->loadThumbnail(sticker->stickerSetOrigin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::validateSelectedIcon(
|
||||||
|
uint64 setId,
|
||||||
|
ValidateIconAnimations animations) {
|
||||||
|
_activeByScrollId = setId;
|
||||||
|
|
||||||
|
auto favedIconIndex = -1;
|
||||||
|
auto newSelected = -1;
|
||||||
|
for (auto i = 0, l = int(_icons.size()); i != l; ++i) {
|
||||||
|
if (_icons[i].setId == setId
|
||||||
|
|| (_icons[i].setId == Data::Stickers::FavedSetId
|
||||||
|
&& setId == Data::Stickers::RecentSetId)) {
|
||||||
|
newSelected = i;
|
||||||
|
break;
|
||||||
|
} else if (_icons[i].setId == Data::Stickers::FavedSetId) {
|
||||||
|
favedIconIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSelectedIcon(
|
||||||
|
(newSelected >= 0
|
||||||
|
? newSelected
|
||||||
|
: (favedIconIndex >= 0) ? favedIconIndex : 0),
|
||||||
|
animations);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::setSelectedIcon(
|
||||||
|
int newSelected,
|
||||||
|
ValidateIconAnimations animations) {
|
||||||
|
if (_iconSel == newSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_iconSel = newSelected;
|
||||||
|
auto iconSelXFinal = _iconSel * st::stickerIconWidth;
|
||||||
|
if (animations == ValidateIconAnimations::Full) {
|
||||||
|
_iconSelX.start(iconSelXFinal);
|
||||||
|
} else {
|
||||||
|
_iconSelX = anim::value(iconSelXFinal, iconSelXFinal);
|
||||||
|
}
|
||||||
|
auto iconsCountForCentering = (2 * _iconSel + 1);
|
||||||
|
auto iconsWidthForCentering = iconsCountForCentering
|
||||||
|
* st::stickerIconWidth;
|
||||||
|
auto iconsXFinal = std::clamp(
|
||||||
|
(_iconsLeft + iconsWidthForCentering + _iconsRight - width()) / 2,
|
||||||
|
0,
|
||||||
|
_iconsMax);
|
||||||
|
if (animations == ValidateIconAnimations::None) {
|
||||||
|
_iconsX = anim::value(iconsXFinal, iconsXFinal);
|
||||||
|
_iconsAnimation.stop();
|
||||||
|
} else {
|
||||||
|
_iconsX.start(iconsXFinal);
|
||||||
|
_iconsStartAnim = crl::now();
|
||||||
|
_iconsAnimation.start();
|
||||||
|
}
|
||||||
|
updateSelected();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::processHideFinished() {
|
||||||
|
_iconOver = _iconDown = SpecialOver::None;
|
||||||
|
_iconsStartAnim = 0;
|
||||||
|
_iconsAnimation.stop();
|
||||||
|
_iconsX.finish();
|
||||||
|
_iconSelX.finish();
|
||||||
|
_horizontal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::leaveToChildEvent(QEvent *e, QWidget *child) {
|
||||||
|
_iconsMousePos = QCursor::pos();
|
||||||
|
updateSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::setLoading(bool loading) {
|
||||||
|
if (_searchCancel) {
|
||||||
|
_searchCancel->setLoadingAnimation(loading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
if (_searchButtonVisible) {
|
||||||
|
paintSearchIcon(p);
|
||||||
|
}
|
||||||
|
if (_icons.empty() || _searchShown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_settingsButtonVisible && !hasOnlyFeaturedSets()) {
|
||||||
|
paintStickerSettingsIcon(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clip = QRect(
|
||||||
|
_iconsLeft,
|
||||||
|
_iconsTop,
|
||||||
|
width() - _iconsLeft - _iconsRight,
|
||||||
|
st::emojiFooterHeight);
|
||||||
|
if (rtl()) {
|
||||||
|
clip.moveLeft(width() - _iconsLeft - clip.width());
|
||||||
|
}
|
||||||
|
p.setClipRect(clip);
|
||||||
|
|
||||||
|
const auto now = crl::now();
|
||||||
|
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::SavedGifs);
|
||||||
|
enumerateVisibleIcons([&](const IconInfo &info) {
|
||||||
|
paintSetIcon(p, info, now, paused);
|
||||||
|
});
|
||||||
|
|
||||||
|
paintSelectionBar(p);
|
||||||
|
paintLeftRightFading(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::paintSelectionBar(Painter &p) const {
|
||||||
|
auto selxrel = _iconsLeft + qRound(_iconSelX.current());
|
||||||
|
auto selx = selxrel - qRound(_iconsX.current());
|
||||||
|
if (rtl()) {
|
||||||
|
selx = width() - selx - st::stickerIconWidth;
|
||||||
|
}
|
||||||
|
p.fillRect(
|
||||||
|
selx,
|
||||||
|
_iconsTop + st::emojiFooterHeight - st::stickerIconPadding,
|
||||||
|
st::stickerIconWidth,
|
||||||
|
st::stickerIconSel,
|
||||||
|
st::stickerIconSelColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::paintLeftRightFading(Painter &p) const {
|
||||||
|
auto o_left = std::clamp(
|
||||||
|
_iconsX.current() / st::stickerIconLeft.width(),
|
||||||
|
0.,
|
||||||
|
1.);
|
||||||
|
if (o_left > 0) {
|
||||||
|
p.setOpacity(o_left);
|
||||||
|
st::stickerIconLeft.fill(p, style::rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiFooterHeight, width()));
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
auto o_right = std::clamp(
|
||||||
|
(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(),
|
||||||
|
0.,
|
||||||
|
1.);
|
||||||
|
if (o_right > 0) {
|
||||||
|
p.setOpacity(o_right);
|
||||||
|
st::stickerIconRight.fill(p, style::rtlrect(width() - _iconsRight - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiFooterHeight, width()));
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::resizeEvent(QResizeEvent *e) {
|
||||||
|
if (_searchField) {
|
||||||
|
resizeSearchControls();
|
||||||
|
}
|
||||||
|
refreshIconsGeometry(ValidateIconAnimations::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::resizeSearchControls() {
|
||||||
|
Expects(_searchField != nullptr);
|
||||||
|
Expects(_searchCancel != nullptr);
|
||||||
|
|
||||||
|
const auto fieldWidth = width()
|
||||||
|
- st::gifsSearchFieldPosition.x()
|
||||||
|
- st::gifsSearchCancelPosition.x()
|
||||||
|
- st::gifsSearchCancel.width;
|
||||||
|
_searchField->resizeToWidth(fieldWidth);
|
||||||
|
_searchField->moveToLeft(st::gifsSearchFieldPosition.x(), st::gifsSearchFieldPosition.y());
|
||||||
|
_searchCancel->moveToRight(st::gifsSearchCancelPosition.x(), st::gifsSearchCancelPosition.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<uint64> StickersListFooter::setChosen() const {
|
||||||
|
return _setChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> StickersListFooter::openSettingsRequests() const {
|
||||||
|
return _openSettingsRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<StickersListFooter::SearchRequest> StickersListFooter::searchRequests() const {
|
||||||
|
return _searchRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::mousePressEvent(QMouseEvent *e) {
|
||||||
|
if (e->button() != Qt::LeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
|
||||||
|
updateSelected();
|
||||||
|
|
||||||
|
if (_iconOver == SpecialOver::Settings) {
|
||||||
|
_openSettingsRequests.fire({});
|
||||||
|
} else if (_iconOver == SpecialOver::Search) {
|
||||||
|
toggleSearch(true);
|
||||||
|
} else {
|
||||||
|
_iconDown = _iconOver;
|
||||||
|
_iconsMouseDown = _iconsMousePos;
|
||||||
|
_iconsStartX = qRound(_iconsX.current());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::mouseMoveEvent(QMouseEvent *e) {
|
||||||
|
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
|
||||||
|
updateSelected();
|
||||||
|
|
||||||
|
if (!_iconsDragging
|
||||||
|
&& !_icons.empty()
|
||||||
|
&& v::is<int>(_iconDown)) {
|
||||||
|
if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) {
|
||||||
|
_iconsDragging = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_iconsDragging) {
|
||||||
|
auto newX = std::clamp(
|
||||||
|
(rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x())
|
||||||
|
+ _iconsStartX,
|
||||||
|
0,
|
||||||
|
_iconsMax);
|
||||||
|
if (newX != qRound(_iconsX.current())) {
|
||||||
|
_iconsX = anim::value(newX, newX);
|
||||||
|
_iconsStartAnim = 0;
|
||||||
|
_iconsAnimation.stop();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
if (_icons.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto wasDown = std::exchange(_iconDown, SpecialOver::None);
|
||||||
|
|
||||||
|
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
|
||||||
|
if (_iconsDragging) {
|
||||||
|
finishDragging();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelected();
|
||||||
|
if (wasDown == _iconOver) {
|
||||||
|
if (const auto index = std::get_if<int>(&_iconOver)) {
|
||||||
|
_iconSelX = anim::value(
|
||||||
|
*index * st::stickerIconWidth,
|
||||||
|
*index * st::stickerIconWidth);
|
||||||
|
_setChosen.fire_copy(_icons[*index].setId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::finishDragging() {
|
||||||
|
auto newX = std::clamp(
|
||||||
|
_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(),
|
||||||
|
0,
|
||||||
|
_iconsMax);
|
||||||
|
if (newX != qRound(_iconsX.current())) {
|
||||||
|
_iconsX = anim::value(newX, newX);
|
||||||
|
_iconsStartAnim = 0;
|
||||||
|
_iconsAnimation.stop();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
_iconsDragging = false;
|
||||||
|
updateSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StickersListFooter::eventHook(QEvent *e) {
|
||||||
|
if (e->type() == QEvent::TouchBegin) {
|
||||||
|
} else if (e->type() == QEvent::Wheel) {
|
||||||
|
if (!_icons.empty()
|
||||||
|
&& v::is<int>(_iconOver)
|
||||||
|
&& (_iconDown == SpecialOver::None)) {
|
||||||
|
scrollByWheelEvent(static_cast<QWheelEvent*>(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InnerFooter::eventHook(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::scrollByWheelEvent(
|
||||||
|
not_null<QWheelEvent*> e) {
|
||||||
|
auto horizontal = (e->angleDelta().x() != 0);
|
||||||
|
auto vertical = (e->angleDelta().y() != 0);
|
||||||
|
if (horizontal) {
|
||||||
|
_horizontal = true;
|
||||||
|
}
|
||||||
|
auto newX = qRound(_iconsX.current());
|
||||||
|
if (/*_horizontal && */horizontal) {
|
||||||
|
newX = std::clamp(
|
||||||
|
newX - (rtl() ? -1 : 1) * (e->pixelDelta().x()
|
||||||
|
? e->pixelDelta().x()
|
||||||
|
: e->angleDelta().x()),
|
||||||
|
0,
|
||||||
|
_iconsMax);
|
||||||
|
} else if (/*!_horizontal && */vertical) {
|
||||||
|
newX = std::clamp(
|
||||||
|
newX - (e->pixelDelta().y()
|
||||||
|
? e->pixelDelta().y()
|
||||||
|
: e->angleDelta().y()),
|
||||||
|
0,
|
||||||
|
_iconsMax);
|
||||||
|
}
|
||||||
|
if (newX != qRound(_iconsX.current())) {
|
||||||
|
_iconsX = anim::value(newX, newX);
|
||||||
|
_iconsStartAnim = 0;
|
||||||
|
_iconsAnimation.stop();
|
||||||
|
updateSelected();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::clipCallback(
|
||||||
|
Media::Clip::Notification notification,
|
||||||
|
uint64 setId) {
|
||||||
|
using namespace Media::Clip;
|
||||||
|
switch (notification) {
|
||||||
|
case Notification::Reinit: {
|
||||||
|
enumerateIcons([&](const IconInfo &info) {
|
||||||
|
auto &icon = _icons[info.index];
|
||||||
|
if (icon.setId != setId || !icon.webm) {
|
||||||
|
return;
|
||||||
|
} else if (icon.webm->state() == State::Error) {
|
||||||
|
icon.webm.setBad();
|
||||||
|
} else if (!info.visible) {
|
||||||
|
icon.webm = nullptr;
|
||||||
|
} else if (icon.webm->ready() && !icon.webm->started()) {
|
||||||
|
icon.webm->start({
|
||||||
|
.frame = { icon.pixw, icon.pixh },
|
||||||
|
.keepAlpha = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateSetIconAt(info.left);
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Notification::Repaint:
|
||||||
|
updateSetIcon(setId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::updateSelected() {
|
||||||
|
if (_iconDown != SpecialOver::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto p = mapFromGlobal(_iconsMousePos);
|
||||||
|
auto x = p.x(), y = p.y();
|
||||||
|
if (rtl()) x = width() - x;
|
||||||
|
const auto settingsLeft = width() - _iconsRight;
|
||||||
|
const auto searchLeft = _iconsLeft - st::stickerIconWidth;
|
||||||
|
auto newOver = OverState(SpecialOver::None);
|
||||||
|
if (_searchButtonVisible
|
||||||
|
&& x >= searchLeft
|
||||||
|
&& x < searchLeft + st::stickerIconWidth
|
||||||
|
&& y >= _iconsTop
|
||||||
|
&& y < _iconsTop + st::emojiFooterHeight) {
|
||||||
|
newOver = SpecialOver::Search;
|
||||||
|
} else if (_settingsButtonVisible
|
||||||
|
&& x >= settingsLeft
|
||||||
|
&& x < settingsLeft + st::stickerIconWidth
|
||||||
|
&& y >= _iconsTop
|
||||||
|
&& y < _iconsTop + st::emojiFooterHeight) {
|
||||||
|
if (!_icons.empty() && !hasOnlyFeaturedSets()) {
|
||||||
|
newOver = SpecialOver::Settings;
|
||||||
|
}
|
||||||
|
} else if (!_icons.empty()) {
|
||||||
|
if (y >= _iconsTop
|
||||||
|
&& y < _iconsTop + st::emojiFooterHeight
|
||||||
|
&& x >= _iconsLeft
|
||||||
|
&& x < width() - _iconsRight) {
|
||||||
|
x += qRound(_iconsX.current()) - _iconsLeft;
|
||||||
|
if (x < _icons.size() * st::stickerIconWidth) {
|
||||||
|
newOver = qFloor(x / st::stickerIconWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newOver != _iconOver) {
|
||||||
|
if (newOver == SpecialOver::None) {
|
||||||
|
setCursor(style::cur_default);
|
||||||
|
} else if (_iconOver == SpecialOver::None) {
|
||||||
|
setCursor(style::cur_pointer);
|
||||||
|
}
|
||||||
|
_iconOver = newOver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StickersListFooter::getLottieRenderer()
|
||||||
|
-> std::shared_ptr<Lottie::FrameRenderer> {
|
||||||
|
if (auto result = _lottieRenderer.lock()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
auto result = Lottie::MakeFrameRenderer();
|
||||||
|
_lottieRenderer = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::refreshIcons(
|
||||||
|
std::vector<StickerIcon> icons,
|
||||||
|
Fn<std::shared_ptr<Lottie::FrameRenderer>()> renderer,
|
||||||
|
ValidateIconAnimations animations) {
|
||||||
|
_renderer = renderer
|
||||||
|
? std::move(renderer)
|
||||||
|
: [=] { return getLottieRenderer(); };
|
||||||
|
|
||||||
|
auto indices = base::flat_map<uint64, int>();
|
||||||
|
indices.reserve(_icons.size());
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto &entry : _icons) {
|
||||||
|
indices.emplace(entry.setId, index++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &now : icons) {
|
||||||
|
if (const auto i = indices.find(now.setId); i != end(indices)) {
|
||||||
|
auto &was = _icons[i->second];
|
||||||
|
if (now.sticker == was.sticker) {
|
||||||
|
now.webm = std::move(was.webm);
|
||||||
|
now.lottie = std::move(was.lottie);
|
||||||
|
now.lifetime = std::move(was.lifetime);
|
||||||
|
now.savedFrame = std::move(was.savedFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_icons = std::move(icons);
|
||||||
|
refreshIconsGeometry(animations);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::refreshIconsGeometry(
|
||||||
|
ValidateIconAnimations animations) {
|
||||||
|
_iconOver = _iconDown = SpecialOver::None;
|
||||||
|
_iconsX.finish();
|
||||||
|
_iconSelX.finish();
|
||||||
|
_iconsStartAnim = 0;
|
||||||
|
_iconsAnimation.stop();
|
||||||
|
_iconsMax = std::max(
|
||||||
|
_iconsLeft + int(_icons.size()) * st::stickerIconWidth + _iconsRight - width(),
|
||||||
|
0);
|
||||||
|
if (_iconsX.current() > _iconsMax) {
|
||||||
|
_iconsX = anim::value(_iconsMax, _iconsMax);
|
||||||
|
}
|
||||||
|
updateSelected();
|
||||||
|
validateSelectedIcon(_activeByScrollId, animations);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StickersListFooter::hasOnlyFeaturedSets() const {
|
||||||
|
return (_icons.size() == 1)
|
||||||
|
&& (_icons[0].setId == Data::Stickers::FeaturedSetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::paintStickerSettingsIcon(Painter &p) const {
|
||||||
|
const auto settingsLeft = width() - _iconsRight;
|
||||||
|
st::stickersSettings.paint(
|
||||||
|
p,
|
||||||
|
settingsLeft
|
||||||
|
+ (st::stickerIconWidth - st::stickersSettings.width()) / 2,
|
||||||
|
_iconsTop + st::emojiCategory.iconPosition.y(),
|
||||||
|
width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::paintSearchIcon(Painter &p) const {
|
||||||
|
const auto searchLeft = _iconsLeft - st::stickerIconWidth;
|
||||||
|
st::stickersSearch.paint(
|
||||||
|
p,
|
||||||
|
searchLeft + (st::stickerIconWidth - st::stickersSearch.width()) / 2,
|
||||||
|
_iconsTop + st::emojiCategory.iconPosition.y(),
|
||||||
|
width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::validateIconLottieAnimation(
|
||||||
|
const StickerIcon &icon) {
|
||||||
|
icon.ensureMediaCreated();
|
||||||
|
if (icon.lottie
|
||||||
|
|| !icon.sticker
|
||||||
|
|| !HasLottieThumbnail(
|
||||||
|
icon.set ? icon.set->flags : Data::StickersSetFlags(),
|
||||||
|
icon.thumbnailMedia.get(),
|
||||||
|
icon.stickerMedia.get())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto player = LottieThumbnail(
|
||||||
|
icon.thumbnailMedia.get(),
|
||||||
|
icon.stickerMedia.get(),
|
||||||
|
StickerLottieSize::StickersFooter,
|
||||||
|
QSize(
|
||||||
|
st::stickerIconWidth - 2 * st::stickerIconPadding,
|
||||||
|
st::emojiFooterHeight - 2 * st::stickerIconPadding
|
||||||
|
) * cIntRetinaFactor(),
|
||||||
|
_renderer());
|
||||||
|
if (!player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
icon.lottie = std::move(player);
|
||||||
|
|
||||||
|
const auto id = icon.setId;
|
||||||
|
icon.lottie->updates(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
updateSetIcon(id);
|
||||||
|
}, icon.lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::validateIconWebmAnimation(
|
||||||
|
const StickerIcon &icon) {
|
||||||
|
icon.ensureMediaCreated();
|
||||||
|
if (icon.webm
|
||||||
|
|| !icon.sticker
|
||||||
|
|| !HasWebmThumbnail(
|
||||||
|
icon.set ? icon.set->flags : Data::StickersSetFlags(),
|
||||||
|
icon.thumbnailMedia.get(),
|
||||||
|
icon.stickerMedia.get())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto id = icon.setId;
|
||||||
|
auto callback = [=](Media::Clip::Notification notification) {
|
||||||
|
clipCallback(notification, id);
|
||||||
|
};
|
||||||
|
icon.webm = WebmThumbnail(
|
||||||
|
icon.thumbnailMedia.get(),
|
||||||
|
icon.stickerMedia.get(),
|
||||||
|
std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::validateIconAnimation(
|
||||||
|
const StickerIcon &icon) {
|
||||||
|
validateIconWebmAnimation(icon);
|
||||||
|
validateIconLottieAnimation(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::updateSetIcon(uint64 setId) {
|
||||||
|
enumerateVisibleIcons([&](const IconInfo &info) {
|
||||||
|
if (_icons[info.index].setId != setId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateSetIconAt(info.left);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::updateSetIconAt(int left) {
|
||||||
|
update(left, _iconsTop, st::stickerIconWidth, st::emojiFooterHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickersListFooter::paintSetIcon(
|
||||||
|
Painter &p,
|
||||||
|
const IconInfo &info,
|
||||||
|
crl::time now,
|
||||||
|
bool paused) const {
|
||||||
|
const auto &icon = _icons[info.index];
|
||||||
|
if (icon.sticker) {
|
||||||
|
icon.ensureMediaCreated();
|
||||||
|
const_cast<StickersListFooter*>(this)->validateIconAnimation(icon);
|
||||||
|
const auto origin = icon.sticker->stickerSetOrigin();
|
||||||
|
const auto thumb = icon.thumbnailMedia
|
||||||
|
? icon.thumbnailMedia->image()
|
||||||
|
: icon.stickerMedia
|
||||||
|
? icon.stickerMedia->thumbnail()
|
||||||
|
: nullptr;
|
||||||
|
const auto x = info.left + (st::stickerIconWidth - icon.pixw) / 2;
|
||||||
|
const auto y = _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2;
|
||||||
|
if (icon.lottie && icon.lottie->ready()) {
|
||||||
|
const auto frame = icon.lottie->frame();
|
||||||
|
const auto size = frame.size() / cIntRetinaFactor();
|
||||||
|
if (icon.savedFrame.isNull()) {
|
||||||
|
icon.savedFrame = QPixmap::fromImage(frame, Qt::ColorOnly);
|
||||||
|
icon.savedFrame.setDevicePixelRatio(cRetinaFactor());
|
||||||
|
}
|
||||||
|
p.drawImage(
|
||||||
|
QRect(
|
||||||
|
info.left + (st::stickerIconWidth - size.width()) / 2,
|
||||||
|
_iconsTop + (st::emojiFooterHeight - size.height()) / 2,
|
||||||
|
size.width(),
|
||||||
|
size.height()),
|
||||||
|
frame);
|
||||||
|
if (!paused) {
|
||||||
|
icon.lottie->markFrameShown();
|
||||||
|
}
|
||||||
|
} else if (icon.webm && icon.webm->started()) {
|
||||||
|
const auto frame = icon.webm->current(
|
||||||
|
{ .frame = { icon.pixw, icon.pixh }, .keepAlpha = true },
|
||||||
|
paused ? 0 : now);
|
||||||
|
if (icon.savedFrame.isNull()) {
|
||||||
|
icon.savedFrame = frame;
|
||||||
|
icon.savedFrame.setDevicePixelRatio(cRetinaFactor());
|
||||||
|
}
|
||||||
|
p.drawPixmapLeft(x, y, width(), frame);
|
||||||
|
} else if (!icon.savedFrame.isNull() || thumb) {
|
||||||
|
const auto pixmap = !icon.savedFrame.isNull()
|
||||||
|
? icon.savedFrame
|
||||||
|
: (!icon.lottie && thumb)
|
||||||
|
? thumb->pix(icon.pixw, icon.pixh)
|
||||||
|
: QPixmap();
|
||||||
|
if (pixmap.isNull()) {
|
||||||
|
return;
|
||||||
|
} else if (icon.savedFrame.isNull()) {
|
||||||
|
icon.savedFrame = pixmap;
|
||||||
|
}
|
||||||
|
p.drawPixmapLeft(x, y, width(), pixmap);
|
||||||
|
}
|
||||||
|
} else if (icon.megagroup) {
|
||||||
|
const auto size = st::stickerGroupCategorySize;
|
||||||
|
icon.megagroup->paintUserpicLeft(
|
||||||
|
p,
|
||||||
|
icon.megagroupUserpic,
|
||||||
|
info.left + (st::stickerIconWidth - size) / 2,
|
||||||
|
_iconsTop + (st::emojiFooterHeight - size) / 2,
|
||||||
|
width(),
|
||||||
|
st::stickerGroupCategorySize);
|
||||||
|
} else if (icon.setId == Data::Stickers::PremiumSetId) {
|
||||||
|
validatePremiumIcon();
|
||||||
|
const auto size = st::stickersPremium.size();
|
||||||
|
p.drawImage(
|
||||||
|
info.left + (st::stickerIconWidth - size.width()) / 2,
|
||||||
|
_iconsTop + (st::emojiFooterHeight - size.height()) / 2,
|
||||||
|
_premiumIcon);
|
||||||
|
} else {
|
||||||
|
const auto paintedIcon = [&] {
|
||||||
|
if (icon.setId == Data::Stickers::FeaturedSetId) {
|
||||||
|
const auto session = &_controller->session();
|
||||||
|
return session->data().stickers().featuredSetsUnreadCount()
|
||||||
|
? &st::stickersTrendingUnread
|
||||||
|
: &st::stickersTrending;
|
||||||
|
//} else if (setId == Stickers::FavedSetId) {
|
||||||
|
// return &st::stickersFaved;
|
||||||
|
} else if (const auto section = SetIdEmojiSection(icon.setId)) {
|
||||||
|
using Section = Ui::Emoji::Section;
|
||||||
|
switch (*section) {
|
||||||
|
case Section::Recent: return &st::emojiRecent;
|
||||||
|
case Section::People: return &st::emojiPeople;
|
||||||
|
case Section::Nature: return &st::emojiNature;
|
||||||
|
case Section::Food: return &st::emojiFood;
|
||||||
|
case Section::Activity: return &st::emojiActivity;
|
||||||
|
case Section::Travel: return &st::emojiTravel;
|
||||||
|
case Section::Objects: return &st::emojiObjects;
|
||||||
|
case Section::Symbols: return &st::emojiSymbols;
|
||||||
|
}
|
||||||
|
Unexpected("Section in SetIdEmojiSection result.");
|
||||||
|
}
|
||||||
|
return &st::emojiRecent;
|
||||||
|
}();
|
||||||
|
paintedIcon->paint(
|
||||||
|
p,
|
||||||
|
info.left + (st::stickerIconWidth - paintedIcon->width()) / 2,
|
||||||
|
_iconsTop + (st::emojiFooterHeight - paintedIcon->height()) / 2,
|
||||||
|
width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StickersListFooter::iconsAnimationCallback(crl::time now) {
|
||||||
|
if (anim::Disabled()) {
|
||||||
|
now += st::stickerIconMove;
|
||||||
|
}
|
||||||
|
if (_iconsStartAnim) {
|
||||||
|
const auto dt = (now - _iconsStartAnim) / float64(st::stickerIconMove);
|
||||||
|
if (dt >= 1.) {
|
||||||
|
_iconsStartAnim = 0;
|
||||||
|
_iconsX.finish();
|
||||||
|
_iconSelX.finish();
|
||||||
|
} else {
|
||||||
|
_iconsX.update(dt, anim::linear);
|
||||||
|
_iconSelX.update(dt, anim::linear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
|
||||||
|
return (_iconsStartAnim != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ChatHelpers
|
207
Telegram/SourceFiles/chat_helpers/stickers_list_footer.h
Normal file
207
Telegram/SourceFiles/chat_helpers/stickers_list_footer.h
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "media/clip/media_clip_reader.h"
|
||||||
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class InputField;
|
||||||
|
class CrossButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class StickersSet;
|
||||||
|
class StickersSetThumbnailView;
|
||||||
|
class DocumentMedia;
|
||||||
|
class CloudImageView;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Lottie {
|
||||||
|
class SinglePlayer;
|
||||||
|
class FrameRenderer;
|
||||||
|
} // namespace Lottie
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
class SessionController;
|
||||||
|
} // namespace Window
|
||||||
|
|
||||||
|
namespace ChatHelpers {
|
||||||
|
|
||||||
|
enum class ValidateIconAnimations {
|
||||||
|
Full,
|
||||||
|
Scroll,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] uint64 EmojiSectionSetId(Ui::Emoji::Section section);
|
||||||
|
[[nodiscard]] std::optional<Ui::Emoji::Section> SetIdEmojiSection(uint64 id);
|
||||||
|
|
||||||
|
struct StickerIcon {
|
||||||
|
explicit StickerIcon(uint64 setId);
|
||||||
|
StickerIcon(
|
||||||
|
not_null<Data::StickersSet*> set,
|
||||||
|
DocumentData *sticker,
|
||||||
|
int pixw,
|
||||||
|
int pixh);
|
||||||
|
StickerIcon(StickerIcon&&);
|
||||||
|
StickerIcon &operator=(StickerIcon&&);
|
||||||
|
~StickerIcon();
|
||||||
|
|
||||||
|
void ensureMediaCreated() const;
|
||||||
|
|
||||||
|
uint64 setId = 0;
|
||||||
|
Data::StickersSet *set = nullptr;
|
||||||
|
mutable std::unique_ptr<Lottie::SinglePlayer> lottie;
|
||||||
|
mutable Media::Clip::ReaderPointer webm;
|
||||||
|
mutable QPixmap savedFrame;
|
||||||
|
DocumentData *sticker = nullptr;
|
||||||
|
ChannelData *megagroup = nullptr;
|
||||||
|
mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia;
|
||||||
|
mutable std::shared_ptr<Data::DocumentMedia> stickerMedia;
|
||||||
|
mutable std::shared_ptr<Data::CloudImageView> megagroupUserpic;
|
||||||
|
int pixw = 0;
|
||||||
|
int pixh = 0;
|
||||||
|
mutable rpl::lifetime lifetime;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StickersListFooter final : public TabbedSelector::InnerFooter {
|
||||||
|
public:
|
||||||
|
struct Descriptor {
|
||||||
|
not_null<Window::SessionController*> controller;
|
||||||
|
not_null<RpWidget*> parent;
|
||||||
|
bool searchButtonVisible = false;
|
||||||
|
bool settingsButtonVisible = false;
|
||||||
|
};
|
||||||
|
explicit StickersListFooter(Descriptor &&descriptor);
|
||||||
|
|
||||||
|
void preloadImages();
|
||||||
|
void validateSelectedIcon(
|
||||||
|
uint64 setId,
|
||||||
|
ValidateIconAnimations animations);
|
||||||
|
void refreshIcons(
|
||||||
|
std::vector<StickerIcon> icons,
|
||||||
|
Fn<std::shared_ptr<Lottie::FrameRenderer>()> renderer,
|
||||||
|
ValidateIconAnimations animations);
|
||||||
|
[[nodiscard]] bool hasOnlyFeaturedSets() const;
|
||||||
|
|
||||||
|
void leaveToChildEvent(QEvent *e, QWidget *child) override;
|
||||||
|
|
||||||
|
void stealFocus();
|
||||||
|
void returnFocus();
|
||||||
|
void setLoading(bool loading);
|
||||||
|
|
||||||
|
void clearHeavyData();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<uint64> setChosen() const;
|
||||||
|
[[nodiscard]] rpl::producer<> openSettingsRequests() const;
|
||||||
|
|
||||||
|
struct SearchRequest {
|
||||||
|
QString text;
|
||||||
|
bool forced = false;
|
||||||
|
};
|
||||||
|
[[nodiscard]] rpl::producer<SearchRequest> searchRequests() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *e) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
bool eventHook(QEvent *e) override;
|
||||||
|
|
||||||
|
void processHideFinished() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class SpecialOver {
|
||||||
|
None,
|
||||||
|
Search,
|
||||||
|
Settings,
|
||||||
|
};
|
||||||
|
using OverState = std::variant<SpecialOver, int>;
|
||||||
|
struct IconInfo {
|
||||||
|
int index = 0;
|
||||||
|
int left = 0;
|
||||||
|
bool visible = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void enumerateVisibleIcons(Fn<void(const IconInfo &)> callback);
|
||||||
|
void enumerateIcons(Fn<void(const IconInfo &)> callback);
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<Lottie::FrameRenderer> getLottieRenderer();
|
||||||
|
bool iconsAnimationCallback(crl::time now);
|
||||||
|
void setSelectedIcon(
|
||||||
|
int newSelected,
|
||||||
|
ValidateIconAnimations animations);
|
||||||
|
void validateIconLottieAnimation(const StickerIcon &icon);
|
||||||
|
void validateIconWebmAnimation(const StickerIcon &icon);
|
||||||
|
void validateIconAnimation(const StickerIcon &icon);
|
||||||
|
|
||||||
|
void refreshIconsGeometry(ValidateIconAnimations animations);
|
||||||
|
void updateSelected();
|
||||||
|
void updateSetIcon(uint64 setId);
|
||||||
|
void updateSetIconAt(int left);
|
||||||
|
void finishDragging();
|
||||||
|
void paintStickerSettingsIcon(Painter &p) const;
|
||||||
|
void paintSearchIcon(Painter &p) const;
|
||||||
|
void paintSetIcon(
|
||||||
|
Painter &p,
|
||||||
|
const IconInfo &info,
|
||||||
|
crl::time now,
|
||||||
|
bool paused) const;
|
||||||
|
void paintSelectionBar(Painter &p) const;
|
||||||
|
void paintLeftRightFading(Painter &p) const;
|
||||||
|
void validatePremiumIcon() const;
|
||||||
|
|
||||||
|
void initSearch();
|
||||||
|
void toggleSearch(bool visible);
|
||||||
|
void resizeSearchControls();
|
||||||
|
void scrollByWheelEvent(not_null<QWheelEvent*> e);
|
||||||
|
|
||||||
|
void clipCallback(Media::Clip::Notification notification, uint64 setId);
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
const bool _searchButtonVisible = false;
|
||||||
|
const bool _settingsButtonVisible = false;
|
||||||
|
|
||||||
|
static constexpr auto kVisibleIconsCount = 8;
|
||||||
|
|
||||||
|
std::weak_ptr<Lottie::FrameRenderer> _lottieRenderer;
|
||||||
|
std::vector<StickerIcon> _icons;
|
||||||
|
Fn<std::shared_ptr<Lottie::FrameRenderer>()> _renderer;
|
||||||
|
uint64 _activeByScrollId = 0;
|
||||||
|
OverState _iconOver = SpecialOver::None;
|
||||||
|
int _iconSel = 0;
|
||||||
|
OverState _iconDown = SpecialOver::None;
|
||||||
|
bool _iconsDragging = false;
|
||||||
|
Ui::Animations::Basic _iconsAnimation;
|
||||||
|
QPoint _iconsMousePos, _iconsMouseDown;
|
||||||
|
mutable QImage _premiumIcon;
|
||||||
|
int _iconsLeft = 0;
|
||||||
|
int _iconsRight = 0;
|
||||||
|
int _iconsTop = 0;
|
||||||
|
int _iconsStartX = 0;
|
||||||
|
int _iconsMax = 0;
|
||||||
|
anim::value _iconsX;
|
||||||
|
anim::value _iconSelX;
|
||||||
|
crl::time _iconsStartAnim = 0;
|
||||||
|
|
||||||
|
bool _horizontal = false;
|
||||||
|
|
||||||
|
bool _searchShown = false;
|
||||||
|
object_ptr<Ui::InputField> _searchField = { nullptr };
|
||||||
|
object_ptr<Ui::CrossButton> _searchCancel = { nullptr };
|
||||||
|
QPointer<QWidget> _focusTakenFrom;
|
||||||
|
|
||||||
|
rpl::event_stream<> _openSettingsRequests;
|
||||||
|
rpl::event_stream<uint64> _setChosen;
|
||||||
|
rpl::event_stream<SearchRequest> _searchRequests;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ChatHelpers
|
File diff suppressed because it is too large
Load diff
|
@ -47,6 +47,8 @@ enum class Notification;
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
|
|
||||||
struct StickerIcon;
|
struct StickerIcon;
|
||||||
|
enum class ValidateIconAnimations;
|
||||||
|
class StickersListFooter;
|
||||||
|
|
||||||
class StickersListWidget final : public TabbedSelector::Inner {
|
class StickersListWidget final : public TabbedSelector::Inner {
|
||||||
public:
|
public:
|
||||||
|
@ -113,7 +115,6 @@ protected:
|
||||||
int countDesiredHeight(int newWidth) override;
|
int countDesiredHeight(int newWidth) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Footer;
|
|
||||||
struct Sticker;
|
struct Sticker;
|
||||||
struct Set;
|
struct Set;
|
||||||
|
|
||||||
|
@ -231,13 +232,6 @@ private:
|
||||||
std::unique_ptr<Ui::RippleAnimation> createButtonRipple(int section);
|
std::unique_ptr<Ui::RippleAnimation> createButtonRipple(int section);
|
||||||
QPoint buttonRippleTopLeft(int section) const;
|
QPoint buttonRippleTopLeft(int section) const;
|
||||||
|
|
||||||
enum class ValidateIconAnimations {
|
|
||||||
Full,
|
|
||||||
Scroll,
|
|
||||||
None,
|
|
||||||
};
|
|
||||||
void validateSelectedIcon(ValidateIconAnimations animations);
|
|
||||||
|
|
||||||
std::vector<Set> &shownSets();
|
std::vector<Set> &shownSets();
|
||||||
const std::vector<Set> &shownSets() const;
|
const std::vector<Set> &shownSets() const;
|
||||||
int featuredRowHeight() const;
|
int featuredRowHeight() const;
|
||||||
|
@ -313,6 +307,7 @@ private:
|
||||||
void removeFavedSticker(int section, int index);
|
void removeFavedSticker(int section, int index);
|
||||||
void setColumnCount(int count);
|
void setColumnCount(int count);
|
||||||
void refreshFooterIcons();
|
void refreshFooterIcons();
|
||||||
|
void refreshIcons(ValidateIconAnimations animations);
|
||||||
|
|
||||||
void showStickerSetBox(not_null<DocumentData*> document);
|
void showStickerSetBox(not_null<DocumentData*> document);
|
||||||
|
|
||||||
|
@ -362,9 +357,7 @@ private:
|
||||||
base::Timer _updateSetsTimer;
|
base::Timer _updateSetsTimer;
|
||||||
base::flat_set<uint64> _repaintSetsIds;
|
base::flat_set<uint64> _repaintSetsIds;
|
||||||
|
|
||||||
uint64 _removingSetId = 0;
|
StickersListFooter *_footer = nullptr;
|
||||||
|
|
||||||
Footer *_footer = nullptr;
|
|
||||||
int _rowsLeft = 0;
|
int _rowsLeft = 0;
|
||||||
int _columnCount = 1;
|
int _columnCount = 1;
|
||||||
QSize _singleSize;
|
QSize _singleSize;
|
||||||
|
|
|
@ -407,7 +407,7 @@ TabbedSelector::TabbedSelector(
|
||||||
session().data().stickers().emojiSetInstalled(
|
session().data().stickers().emojiSetInstalled(
|
||||||
) | rpl::start_with_next([=](uint64 setId) {
|
) | rpl::start_with_next([=](uint64 setId) {
|
||||||
_tabsSlider->setActiveSection(indexByType(SelectorTab::Emoji));
|
_tabsSlider->setActiveSection(indexByType(SelectorTab::Emoji));
|
||||||
emoji()->showCustomSet(setId);
|
emoji()->showSet(setId);
|
||||||
_showRequests.fire({});
|
_showRequests.fire({});
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue