401 lines
10 KiB
C++
401 lines
10 KiB
C++
/*
|
|
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 "chat_helpers/tabbed_selector.h"
|
|
#include "data/stickers/data_stickers.h"
|
|
#include "base/variant.h"
|
|
#include "base/timer.h"
|
|
|
|
namespace Main {
|
|
class Session;
|
|
} // namespace Main
|
|
|
|
namespace Window {
|
|
class SessionController;
|
|
} // namespace Window
|
|
|
|
namespace Ui {
|
|
class LinkButton;
|
|
class PopupMenu;
|
|
class RippleAnimation;
|
|
class BoxContent;
|
|
class PathShiftGradient;
|
|
} // namespace Ui
|
|
|
|
namespace Lottie {
|
|
class Animation;
|
|
class MultiPlayer;
|
|
class FrameRenderer;
|
|
} // namespace Lottie
|
|
|
|
namespace Data {
|
|
class DocumentMedia;
|
|
class StickersSet;
|
|
} // namespace Data
|
|
|
|
namespace ChatHelpers {
|
|
|
|
struct StickerIcon;
|
|
|
|
class StickersListWidget
|
|
: public TabbedSelector::Inner
|
|
, private base::Subscriber {
|
|
public:
|
|
StickersListWidget(
|
|
QWidget *parent,
|
|
not_null<Window::SessionController*> controller,
|
|
bool masks = false);
|
|
|
|
Main::Session &session() const;
|
|
|
|
rpl::producer<TabbedSelector::FileChosen> chosen() const;
|
|
rpl::producer<> scrollUpdated() const;
|
|
rpl::producer<> checkForHide() const;
|
|
|
|
void refreshRecent() override;
|
|
void preloadImages() override;
|
|
void clearSelection() override;
|
|
object_ptr<TabbedSelector::InnerFooter> createFooter() override;
|
|
|
|
void showStickerSet(uint64 setId);
|
|
void showMegagroupSet(ChannelData *megagroup);
|
|
|
|
void afterShown() override;
|
|
void beforeHiding() override;
|
|
|
|
void refreshStickers();
|
|
|
|
std::vector<StickerIcon> fillIcons();
|
|
bool preventAutoHide();
|
|
|
|
uint64 currentSet(int yOffset) const;
|
|
|
|
void installedLocally(uint64 setId);
|
|
void notInstalledLocally(uint64 setId);
|
|
void clearInstalledLocally();
|
|
|
|
void sendSearchRequest();
|
|
void searchForSets(const QString &query);
|
|
|
|
std::shared_ptr<Lottie::FrameRenderer> getLottieRenderer();
|
|
|
|
void fillContextMenu(
|
|
not_null<Ui::PopupMenu*> menu,
|
|
SendMenu::Type type) override;
|
|
|
|
bool mySetsEmpty() const;
|
|
|
|
~StickersListWidget();
|
|
|
|
protected:
|
|
void visibleTopBottomUpdated(
|
|
int visibleTop,
|
|
int visibleBottom) override;
|
|
|
|
void mousePressEvent(QMouseEvent *e) override;
|
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
|
void mouseMoveEvent(QMouseEvent *e) override;
|
|
void resizeEvent(QResizeEvent *e) override;
|
|
void paintEvent(QPaintEvent *e) override;
|
|
void leaveEventHook(QEvent *e) override;
|
|
void leaveToChildEvent(QEvent *e, QWidget *child) override;
|
|
void enterFromChildEvent(QEvent *e, QWidget *child) override;
|
|
|
|
TabbedSelector::InnerFooter *getFooter() const override;
|
|
void processHideFinished() override;
|
|
void processPanelHideFinished() override;
|
|
int countDesiredHeight(int newWidth) override;
|
|
|
|
private:
|
|
class Footer;
|
|
|
|
enum class Section {
|
|
Featured,
|
|
Stickers,
|
|
Search,
|
|
};
|
|
|
|
struct OverSticker {
|
|
int section = 0;
|
|
int index = 0;
|
|
bool overDelete = false;
|
|
|
|
inline bool operator==(OverSticker other) const {
|
|
return (section == other.section)
|
|
&& (index == other.index)
|
|
&& (overDelete == other.overDelete);
|
|
}
|
|
inline bool operator!=(OverSticker other) const {
|
|
return !(*this == other);
|
|
}
|
|
};
|
|
struct OverSet {
|
|
int section = 0;
|
|
|
|
inline bool operator==(OverSet other) const {
|
|
return (section == other.section);
|
|
}
|
|
inline bool operator!=(OverSet other) const {
|
|
return !(*this == other);
|
|
}
|
|
};
|
|
struct OverButton {
|
|
int section = 0;
|
|
|
|
inline bool operator==(OverButton other) const {
|
|
return (section == other.section);
|
|
}
|
|
inline bool operator!=(OverButton other) const {
|
|
return !(*this == other);
|
|
}
|
|
};
|
|
struct OverGroupAdd {
|
|
inline bool operator==(OverGroupAdd other) const {
|
|
return true;
|
|
}
|
|
inline bool operator!=(OverGroupAdd other) const {
|
|
return !(*this == other);
|
|
}
|
|
};
|
|
using OverState = std::variant<
|
|
v::null_t,
|
|
OverSticker,
|
|
OverSet,
|
|
OverButton,
|
|
OverGroupAdd>;
|
|
|
|
struct SectionInfo {
|
|
int section = 0;
|
|
int count = 0;
|
|
int top = 0;
|
|
int rowsCount = 0;
|
|
int rowsTop = 0;
|
|
int rowsBottom = 0;
|
|
};
|
|
|
|
struct Sticker {
|
|
not_null<DocumentData*> document;
|
|
std::shared_ptr<Data::DocumentMedia> documentMedia;
|
|
Lottie::Animation *animated = nullptr;
|
|
QPixmap savedFrame;
|
|
|
|
void ensureMediaCreated();
|
|
};
|
|
|
|
struct Set {
|
|
Set(
|
|
uint64 id,
|
|
Data::StickersSet *set,
|
|
Data::StickersSetFlags flags,
|
|
const QString &title,
|
|
const QString &shortName,
|
|
int count,
|
|
bool externalLayout,
|
|
std::vector<Sticker> &&stickers = {});
|
|
Set(Set &&other);
|
|
Set &operator=(Set &&other);
|
|
~Set();
|
|
|
|
uint64 id = 0;
|
|
Data::StickersSet *set = nullptr;
|
|
Data::StickersSetFlags flags;
|
|
QString title;
|
|
QString shortName;
|
|
std::vector<Sticker> stickers;
|
|
std::unique_ptr<Ui::RippleAnimation> ripple;
|
|
|
|
std::unique_ptr<Lottie::MultiPlayer> lottiePlayer;
|
|
rpl::lifetime lottieLifetime;
|
|
|
|
int count = 0;
|
|
bool externalLayout = false;
|
|
};
|
|
struct FeaturedSet {
|
|
uint64 id = 0;
|
|
Data::StickersSetFlags flags;
|
|
std::vector<Sticker> stickers;
|
|
};
|
|
|
|
static std::vector<Sticker> PrepareStickers(
|
|
const QVector<DocumentData*> &pack);
|
|
|
|
void preloadMoreOfficial();
|
|
QSize boundingBoxSize() const;
|
|
|
|
template <typename Callback>
|
|
bool enumerateSections(Callback callback) const;
|
|
SectionInfo sectionInfo(int section) const;
|
|
SectionInfo sectionInfoByOffset(int yOffset) const;
|
|
|
|
void setSection(Section section);
|
|
void displaySet(uint64 setId);
|
|
void checkHideWithBox(QPointer<Ui::BoxContent> box);
|
|
void installSet(uint64 setId);
|
|
void removeMegagroupSet(bool locally);
|
|
void removeSet(uint64 setId);
|
|
void sendInstallRequest(
|
|
uint64 setId,
|
|
const MTPInputStickerSet &input);
|
|
void refreshMySets();
|
|
void refreshFeaturedSets();
|
|
void refreshSearchSets();
|
|
void refreshSearchIndex();
|
|
|
|
bool setHasTitle(const Set &set) const;
|
|
bool stickerHasDeleteButton(const Set &set, int index) const;
|
|
std::vector<Sticker> collectRecentStickers();
|
|
void refreshRecentStickers(bool resize = true);
|
|
void refreshFavedStickers();
|
|
enum class GroupStickersPlace {
|
|
Visible,
|
|
Hidden,
|
|
};
|
|
void refreshMegagroupStickers(GroupStickersPlace place);
|
|
void refreshSettingsVisibility();
|
|
|
|
void updateSelected();
|
|
void setSelected(OverState newSelected);
|
|
void setPressed(OverState newPressed);
|
|
std::unique_ptr<Ui::RippleAnimation> createButtonRipple(int section);
|
|
QPoint buttonRippleTopLeft(int section) const;
|
|
|
|
enum class ValidateIconAnimations {
|
|
Full,
|
|
Scroll,
|
|
None,
|
|
};
|
|
void validateSelectedIcon(ValidateIconAnimations animations);
|
|
|
|
std::vector<Set> &shownSets();
|
|
const std::vector<Set> &shownSets() const;
|
|
int featuredRowHeight() const;
|
|
void checkVisibleFeatured(int visibleTop, int visibleBottom);
|
|
void readVisibleFeatured(int visibleTop, int visibleBottom);
|
|
|
|
void paintStickers(Painter &p, QRect clip);
|
|
void paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected);
|
|
void paintSticker(Painter &p, Set &set, int y, int section, int index, bool selected, bool deleteSelected);
|
|
void paintEmptySearchResults(Painter &p);
|
|
|
|
void ensureLottiePlayer(Set &set);
|
|
void setupLottie(Set &set, int section, int index);
|
|
void markLottieFrameShown(Set &set);
|
|
void checkVisibleLottie();
|
|
void pauseInvisibleLottieIn(const SectionInfo &info);
|
|
void takeHeavyData(std::vector<Set> &to, std::vector<Set> &from);
|
|
void takeHeavyData(Set &to, Set &from);
|
|
void takeHeavyData(Sticker &to, Sticker &from);
|
|
void clearHeavyIn(Set &set, bool clearSavedFrames = true);
|
|
void clearHeavyData();
|
|
|
|
int stickersRight() const;
|
|
bool featuredHasAddButton(int index) const;
|
|
QRect featuredAddRect(int index) const;
|
|
bool hasRemoveButton(int index) const;
|
|
QRect removeButtonRect(int index) const;
|
|
int megagroupSetInfoLeft() const;
|
|
void refreshMegagroupSetGeometry();
|
|
QRect megagroupSetButtonRectFinal() const;
|
|
|
|
const Data::StickersSetsOrder &defaultSetsOrder() const;
|
|
Data::StickersSetsOrder &defaultSetsOrderRef();
|
|
|
|
enum class AppendSkip {
|
|
None,
|
|
Archived,
|
|
Installed,
|
|
};
|
|
bool appendSet(
|
|
std::vector<Set> &to,
|
|
uint64 setId,
|
|
bool externalLayout,
|
|
AppendSkip skip = AppendSkip::None);
|
|
|
|
void selectEmoji(EmojiPtr emoji);
|
|
int stickersLeft() const;
|
|
QRect stickerRect(int section, int sel);
|
|
|
|
void removeRecentSticker(int section, int index);
|
|
void removeFavedSticker(int section, int index);
|
|
void setColumnCount(int count);
|
|
void refreshFooterIcons();
|
|
|
|
void showStickerSetBox(not_null<DocumentData*> document);
|
|
|
|
void cancelSetsSearch();
|
|
void showSearchResults();
|
|
void searchResultsDone(const MTPmessages_FoundStickerSets &result);
|
|
void refreshSearchRows();
|
|
void refreshSearchRows(const std::vector<uint64> *cloudSets);
|
|
void fillLocalSearchRows(const QString &query);
|
|
void fillCloudSearchRows(const std::vector<uint64> &cloudSets);
|
|
void addSearchRow(not_null<Data::StickersSet*> set);
|
|
|
|
void showPreview();
|
|
|
|
MTP::Sender _api;
|
|
ChannelData *_megagroupSet = nullptr;
|
|
uint64 _megagroupSetIdRequested = 0;
|
|
std::vector<Set> _mySets;
|
|
std::vector<Set> _officialSets;
|
|
std::vector<Set> _searchSets;
|
|
int _featuredSetsCount = 0;
|
|
base::flat_set<uint64> _installedLocallySets;
|
|
std::vector<bool> _custom;
|
|
base::flat_set<not_null<DocumentData*>> _favedStickersMap;
|
|
std::weak_ptr<Lottie::FrameRenderer> _lottieRenderer;
|
|
|
|
mtpRequestId _officialRequestId = 0;
|
|
int _officialOffset = 0;
|
|
|
|
Section _section = Section::Stickers;
|
|
const bool _isMasks;
|
|
|
|
bool _displayingSet = false;
|
|
uint64 _removingSetId = 0;
|
|
|
|
Footer *_footer = nullptr;
|
|
int _rowsLeft = 0;
|
|
int _columnCount = 1;
|
|
QSize _singleSize;
|
|
|
|
OverState _selected;
|
|
OverState _pressed;
|
|
QPoint _lastMousePosition;
|
|
|
|
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
|
|
|
Ui::Text::String _megagroupSetAbout;
|
|
QString _megagroupSetButtonText;
|
|
int _megagroupSetButtonTextWidth = 0;
|
|
QRect _megagroupSetButtonRect;
|
|
std::unique_ptr<Ui::RippleAnimation> _megagroupSetButtonRipple;
|
|
|
|
QString _addText;
|
|
int _addWidth;
|
|
|
|
object_ptr<Ui::LinkButton> _settings;
|
|
|
|
base::Timer _previewTimer;
|
|
bool _previewShown = false;
|
|
|
|
std::map<QString, std::vector<uint64>> _searchCache;
|
|
std::vector<std::pair<uint64, QStringList>> _searchIndex;
|
|
base::Timer _searchRequestTimer;
|
|
QString _searchQuery, _searchNextQuery;
|
|
mtpRequestId _searchRequestId = 0;
|
|
|
|
rpl::event_stream<TabbedSelector::FileChosen> _chosen;
|
|
rpl::event_stream<> _scrollUpdated;
|
|
rpl::event_stream<> _checkForHide;
|
|
|
|
};
|
|
|
|
} // namespace ChatHelpers
|