Show sticker effects in a StickerListWidget.
This commit is contained in:
parent
5fb7992b04
commit
b92a05011f
|
@ -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 }};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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){
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue