Show sticker effects in a StickerListWidget.

This commit is contained in:
John Preston 2024-05-12 12:49:28 +04:00
parent 5fb7992b04
commit b92a05011f
8 changed files with 163 additions and 34 deletions

View File

@ -489,6 +489,7 @@ hashtagClose: IconButton {
stickerPanWidthMin: 64px;
stickerPanSize: size(stickerPanWidthMin, stickerPanWidthMin);
stickerEffectWidthMin: 48px;
stickerPanPadding: 11px;
stickerPanDeleteIconBg: icon {{ "emoji/emoji_delete_bg", stickerPanDeleteBg }};
stickerPanDeleteIconFg: icon {{ "emoji/emoji_delete", stickerPanDeleteFg }};

View File

@ -189,8 +189,10 @@ StickersListWidget::StickersListWidget(
, _overBg(st::roundRadiusLarge, st().overBg)
, _api(&session().mtp())
, _localSetsManager(std::make_unique<LocalStickersManager>(&session()))
, _customRecentIds(std::move(descriptor.customRecentList))
, _section(Section::Stickers)
, _isMasks(_mode == Mode::Masks)
, _isEffects(_mode == Mode::MessageEffects)
, _updateItemsTimer([=] { updateItems(); })
, _updateSetsTimer([=] { updateSets(); })
, _trendingAddBgOver(
@ -220,9 +222,11 @@ StickersListWidget::StickersListWidget(
, _premiumMark(std::make_unique<StickerPremiumMark>(&session()))
, _searchRequestTimer([=] { sendSearchRequest(); }) {
setMouseTracking(true);
setAttribute(Qt::WA_OpaquePaintEvent);
if (st().bg->c.alpha() > 0) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
if (!_isMasks) {
if (!_isMasks && !_isEffects) {
setupSearch();
}
@ -254,23 +258,30 @@ StickersListWidget::StickersListWidget(
refreshStickers();
}, lifetime());
session().data().stickers().recentUpdated(
_isMasks ? Data::StickersType::Masks : Data::StickersType::Stickers
) | rpl::start_with_next([=] {
refreshRecent();
}, lifetime());
if (!_isEffects) {
session().data().stickers().recentUpdated(_isMasks
? Data::StickersType::Masks
: Data::StickersType::Stickers
) | rpl::start_with_next([=] {
refreshRecent();
}, lifetime());
}
positionValue(
) | rpl::skip(1) | rpl::map_to(
TabbedSelector::Action::Update
) | rpl::start_to_stream(_choosingUpdated, lifetime());
rpl::merge(
Data::AmPremiumValue(&session()) | rpl::to_empty,
session().api().premium().cloudSetUpdated()
) | rpl::start_with_next([=] {
if (_isEffects) {
refreshStickers();
}, lifetime());
} else {
rpl::merge(
Data::AmPremiumValue(&session()) | rpl::to_empty,
session().api().premium().cloudSetUpdated()
) | rpl::start_with_next([=] {
refreshStickers();
}, lifetime());
}
}
rpl::producer<FileChosen> StickersListWidget::chosen() const {
@ -498,11 +509,14 @@ StickersListWidget::SectionInfo StickersListWidget::sectionInfoByOffset(int yOff
}
int StickersListWidget::countDesiredHeight(int newWidth) {
if (newWidth <= st::stickerPanWidthMin) {
const auto minSize = _isEffects
? st::stickerEffectWidthMin
: st::stickerPanWidthMin;
if (newWidth < 2 * minSize) {
return 0;
}
auto availableWidth = newWidth - (st::stickerPanPadding - st().margin.left());
auto columnCount = availableWidth / st::stickerPanWidthMin;
auto columnCount = availableWidth / minSize;
auto singleWidth = availableWidth / columnCount;
auto fullWidth = (st().margin.left() + newWidth + st::emojiScroll.width);
auto rowsRight = (fullWidth - columnCount * singleWidth) / 2;
@ -872,7 +886,9 @@ QRect StickersListWidget::stickerRect(int section, int sel) {
void StickersListWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
auto clip = e->rect();
p.fillRect(clip, st().bg);
if (st().bg->c.alpha() > 0) {
p.fillRect(clip, st().bg);
}
paintStickers(p, clip);
}
@ -1459,7 +1475,21 @@ void StickersListWidget::paintSticker(
p.setOpacity(1.);
}
if (premium) {
auto cornerPainted = false;
if (set.id == Data::Stickers::RecentSetId && !_cornerEmoji.empty()) {
Assert(index < _cornerEmoji.size());
if (const auto emoji = _cornerEmoji[index]) {
const auto size = Ui::Emoji::GetSizeNormal();
const auto ratio = style::DevicePixelRatio();
const auto radius = st::roundRadiusSmall;
const auto position = pos
+ QPoint(_singleSize.width(), _singleSize.height())
- QPoint(size / ratio + radius, size / ratio + radius);
Ui::Emoji::Draw(p, emoji, size, position.x(), position.y());
cornerPainted = true;
}
}
if (!cornerPainted && premium) {
_premiumMark->paint(
p,
lottieFrame,
@ -1928,10 +1958,13 @@ void StickersListWidget::clearHeavyData() {
void StickersListWidget::refreshStickers() {
clearSelection();
refreshMySets();
refreshFeaturedSets();
refreshSearchSets();
if (_isEffects) {
refreshEffects();
} else {
refreshMySets();
refreshFeaturedSets();
refreshSearchSets();
}
resizeToWidth(width());
if (_footer) {
@ -1946,6 +1979,13 @@ void StickersListWidget::refreshStickers() {
visibleTopBottomUpdated(getVisibleTop(), getVisibleBottom());
}
void StickersListWidget::refreshEffects() {
auto wasSets = base::take(_mySets);
_mySets.reserve(1);
refreshRecentStickers(false);
takeHeavyData(_mySets, wasSets);
}
void StickersListWidget::refreshMySets() {
auto wasSets = base::take(_mySets);
_favedStickersMap.clear();
@ -2107,7 +2147,27 @@ void StickersListWidget::refreshRecent() {
}
}
auto StickersListWidget::collectCustomRecents() -> std::vector<Sticker> {
_custom.clear();
_cornerEmoji.clear();
auto result = std::vector<Sticker>();
result.reserve(_customRecentIds.size());
const auto owner = &session().data();
for (const auto &descriptor : _customRecentIds) {
if (const auto document = descriptor.document; document->sticker()) {
result.push_back(Sticker{ document });
_custom.push_back(false);
_cornerEmoji.push_back(Ui::Emoji::Find(descriptor.cornerEmoji));
}
}
return result;
}
auto StickersListWidget::collectRecentStickers() -> std::vector<Sticker> {
if (_isEffects) {
return collectCustomRecents();
}
_custom.clear();
auto result = std::vector<Sticker>();
@ -2435,7 +2495,9 @@ bool StickersListWidget::setHasTitle(const Set &set) const {
return false;
} else if (set.id == Data::Stickers::RecentSetId) {
return !_mySets.empty()
&& (_isMasks || (_mySets[0].id == Data::Stickers::FavedSetId));
&& (_isMasks
|| _isEffects
|| (_mySets[0].id == Data::Stickers::FavedSetId));
}
return true;
}

View File

@ -66,12 +66,19 @@ enum class StickersListMode {
Masks,
UserpicBuilder,
ChatIntro,
MessageEffects,
};
struct StickerCustomRecentDescriptor {
not_null<DocumentData*> document;
QString cornerEmoji;
};
struct StickersListDescriptor {
std::shared_ptr<Show> show;
StickersListMode mode = StickersListMode::Full;
Fn<bool()> paused;
std::vector<StickerCustomRecentDescriptor> customRecentList;
const style::EmojiPan *st = nullptr;
ComposeFeatures features;
};
@ -239,8 +246,10 @@ private:
bool setHasTitle(const Set &set) const;
bool stickerHasDeleteButton(const Set &set, int index) const;
std::vector<Sticker> collectRecentStickers();
[[nodiscard]] std::vector<Sticker> collectRecentStickers();
[[nodiscard]] std::vector<Sticker> collectCustomRecents();
void refreshRecentStickers(bool resize = true);
void refreshEffects();
void refreshFavedStickers();
enum class GroupStickersPlace {
Visible,
@ -364,11 +373,13 @@ private:
std::unique_ptr<LocalStickersManager> _localSetsManager;
ChannelData *_megagroupSet = nullptr;
uint64 _megagroupSetIdRequested = 0;
std::vector<StickerCustomRecentDescriptor> _customRecentIds;
std::vector<Set> _mySets;
std::vector<Set> _officialSets;
std::vector<Set> _searchSets;
int _featuredSetsCount = 0;
std::vector<bool> _custom;
std::vector<EmojiPtr> _cornerEmoji;
base::flat_set<not_null<DocumentData*>> _favedStickersMap;
std::weak_ptr<Lottie::FrameRenderer> _lottieRenderer;
@ -381,6 +392,7 @@ private:
Section _section = Section::Stickers;
const bool _isMasks;
const bool _isEffects;
base::Timer _updateItemsTimer;
base::Timer _updateSetsTimer;

View File

@ -249,6 +249,9 @@ PossibleItemReactions::PossibleItemReactions(
: recent(other.recent | ranges::views::transform([](const auto &value) {
return *value;
}) | ranges::to_vector)
, stickers(other.stickers | ranges::views::transform([](const auto &value) {
return *value;
}) | ranges::to_vector)
, customAllowed(other.customAllowed)
, tags(other.tags){
}

View File

@ -43,6 +43,7 @@ struct Reaction {
struct PossibleItemReactionsRef {
std::vector<not_null<const Reaction*>> recent;
std::vector<not_null<const Reaction*>> stickers;
bool customAllowed = false;
bool tags = false;
};
@ -52,6 +53,7 @@ struct PossibleItemReactions {
explicit PossibleItemReactions(const PossibleItemReactionsRef &other);
std::vector<Reaction> recent;
std::vector<Reaction> stickers;
bool customAllowed = false;
bool tags = false;
};

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/text/text_custom_emoji.h"
#include "ui/text/text_utilities.h"
#include "ui/platform/ui_platform_utility.h"
@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "menu/menu_send.h"
#include "chat_helpers/emoji_list_widget.h"
#include "chat_helpers/stickers_list_footer.h"
#include "chat_helpers/stickers_list_widget.h"
#include "window/window_session_controller.h"
#include "boxes/premium_preview_box.h"
#include "mainwidget.h"
@ -217,6 +219,7 @@ Selector::Selector(
child) {
}
#if 0 // not ready
Selector::Selector(
not_null<QWidget*> parent,
const style::EmojiPan &st,
@ -237,6 +240,7 @@ Selector::Selector(
close,
child) {
}
#endif
Selector::Selector(
not_null<QWidget*> parent,
@ -931,8 +935,10 @@ void Selector::createList() {
if (!_reactions.customAllowed) {
st->bg = st::transparent;
}
_list = _scroll->setOwnedWidget(
object_ptr<EmojiListWidget>(_scroll, EmojiListDescriptor{
auto lists = _scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(_scroll));
_list = lists->add(
object_ptr<EmojiListWidget>(lists, EmojiListDescriptor{
.show = _show,
.mode = _listMode,
.paused = [] { return false; },
@ -941,12 +947,35 @@ void Selector::createList() {
: _recent),
.customRecentFactory = _unifiedFactoryOwner->factory(),
.st = st,
})
).data();
}));
if (!_reactions.stickers.empty()) {
auto descriptors = ranges::views::all(
_reactions.stickers
) | ranges::view::transform([](const Data::Reaction &reaction) {
return ChatHelpers::StickerCustomRecentDescriptor{
reaction.selectAnimation,
reaction.title
};
}) | ranges::to_vector;
_stickers = lists->add(
object_ptr<StickersListWidget>(
lists,
StickersListDescriptor{
.show = _show,
.mode = StickersListMode::MessageEffects,
.paused = [] { return false; },
.customRecentList = std::move(descriptors),
.st = st,
}));
}
_list->escapes() | rpl::start_to_stream(_escapes, _list->lifetime());
_list->customChosen(
rpl::merge(
_list->customChosen(),
(_stickers
? _stickers->chosen()
: rpl::never<ChatHelpers::FileChosen>())
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
_chosen.fire({
.id = _unifiedFactoryOwner->lookupReactionId(data.document->id),
@ -986,24 +1015,35 @@ void Selector::createList() {
_shadow->show();
}
const auto geometry = inner.marginsRemoved(_st.margin);
_list->move(0, 0);
_list->resizeToWidth(geometry.width());
lists->move(0, 0);
lists->resizeToWidth(geometry.width());
_list->refreshEmoji();
_list->show();
lists->show();
const auto updateVisibleTopBottom = [=] {
const auto scrollTop = _scroll->scrollTop();
const auto scrollBottom = scrollTop + _scroll->height();
_list->setVisibleTopBottom(scrollTop, scrollBottom);
lists->setVisibleTopBottom(scrollTop, scrollBottom);
};
_scroll->scrollTopChanges(
) | rpl::start_with_next(updateVisibleTopBottom, _list->lifetime());
) | rpl::start_with_next(updateVisibleTopBottom, lists->lifetime());
_list->scrollToRequests(
) | rpl::start_with_next([=](int y) {
_scroll->scrollToY(y);
_shadow->update();
if (_shadow) {
_shadow->update();
}
}, _list->lifetime());
if (_stickers) {
_stickers->scrollToRequests(
) | rpl::start_with_next([=](int y) {
_scroll->scrollToY(_list->height() + y);
if (_shadow) {
_shadow->update();
}
}, _stickers->lifetime());
}
_scroll->setGeometry(inner.marginsRemoved({
_st.margin.left(),

View File

@ -24,6 +24,7 @@ namespace ChatHelpers {
class Show;
class TabbedPanel;
class EmojiListWidget;
class StickersListWidget;
class StickersListFooter;
enum class EmojiListMode;
} // namespace ChatHelpers
@ -85,6 +86,7 @@ public:
Fn<void(bool fast)> close,
IconFactory iconFactory = nullptr,
bool child = false);
#if 0 // not ready
Selector(
not_null<QWidget*> parent,
const style::EmojiPan &st,
@ -93,6 +95,7 @@ public:
std::vector<DocumentId> recent,
Fn<void(bool fast)> close,
bool child = false);
#endif
~Selector();
[[nodiscard]] bool useTransparency() const;
@ -193,6 +196,7 @@ private:
Ui::ScrollArea *_scroll = nullptr;
ChatHelpers::EmojiListWidget *_list = nullptr;
ChatHelpers::StickersListWidget *_stickers = nullptr;
ChatHelpers::StickersListFooter *_footer = nullptr;
std::unique_ptr<UnifiedFactoryOwner> _unifiedFactoryOwner;
Ui::PlainShadow *_shadow = nullptr;

View File

@ -169,10 +169,15 @@ void BottomRounded::paintEvent(QPaintEvent *e) {
const auto premiumPossible = session->premiumPossible();
auto added = base::flat_set<Data::ReactionId>();
result.recent.reserve(effects.size());
result.stickers.reserve(effects.size());
for (const auto &reaction : effects) {
if (premiumPossible || !reaction.premium) {
if (added.emplace(reaction.id).second) {
result.recent.push_back(&reaction);
if (reaction.aroundAnimation) {
result.recent.push_back(&reaction);
} else {
result.stickers.push_back(&reaction);
}
}
}
}