Use StickersListFooter for GIFs section.

This commit is contained in:
John Preston 2023-01-24 15:09:11 +04:00
parent d51d1939b0
commit ffb2c5093d
7 changed files with 177 additions and 93 deletions

View file

@ -353,8 +353,6 @@ gifsSearchField: defaultMultiSelectSearchField;
gifsSearchFieldPosition: point(42px, 7px);
gifsSearchCancel: defaultMultiSelectSearchCancel;
gifsSearchCancelPosition: point(1px, 1px);
gifsSearchIcon: boxFieldSearchIcon;
gifsSearchIconPosition: point(6px, 7px);
emojiSuggestionsDropdown: InnerDropdown(defaultInnerDropdown) {
scrollMargin: margins(0px, emojiColorsPadding, 0px, emojiColorsPadding);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_toggling_media.h" // Api::ToggleSavedGif
#include "base/const_string.h"
#include "base/qt/qt_key_modifiers.h"
#include "chat_helpers/stickers_list_footer.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_emoji_statuses.h"
@ -83,94 +84,6 @@ void AddGifAction(
}, saved ? &st::menuIconDelete : &st::menuIconGif);
}
class GifsListWidget::Footer : public TabbedSelector::InnerFooter {
public:
Footer(not_null<GifsListWidget*> parent);
void stealFocus();
void returnFocus();
void setLoading(bool loading) {
_cancel->setLoadingAnimation(loading);
}
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void processPanelHideFinished() override;
private:
not_null<GifsListWidget*> _pan;
object_ptr<Ui::InputField> _field;
object_ptr<Ui::CrossButton> _cancel;
QPointer<QWidget> _focusTakenFrom;
};
GifsListWidget::Footer::Footer(not_null<GifsListWidget*> parent)
: InnerFooter(parent, st::defaultEmojiPan)
, _pan(parent)
, _field(this, st::gifsSearchField, tr::lng_gifs_search())
, _cancel(this, st::gifsSearchCancel) {
connect(_field, &Ui::InputField::submitted, [=] {
_pan->sendInlineRequest();
});
connect(_field, &Ui::InputField::cancelled, [=] {
if (_field->getLastText().isEmpty()) {
_pan->cancelled();
} else {
_field->setText(QString());
}
});
connect(_field, &Ui::InputField::changed, [=] {
_cancel->toggle(
!_field->getLastText().isEmpty(),
anim::type::normal);
_pan->searchForGifs(_field->getLastText());
});
_cancel->setClickedCallback([=] {
_field->setText(QString());
});
}
void GifsListWidget::Footer::stealFocus() {
if (!_focusTakenFrom) {
_focusTakenFrom = QApplication::focusWidget();
}
_field->setFocus();
}
void GifsListWidget::Footer::returnFocus() {
if (_focusTakenFrom) {
if (_field->hasFocus()) {
_focusTakenFrom->setFocus();
}
_focusTakenFrom = nullptr;
}
}
void GifsListWidget::Footer::paintEvent(QPaintEvent *e) {
Painter p(this);
st::gifsSearchIcon.paint(p, st::gifsSearchIconPosition.x(), st::gifsSearchIconPosition.y(), width());
}
void GifsListWidget::Footer::resizeEvent(QResizeEvent *e) {
auto fieldWidth = width()
- st::gifsSearchFieldPosition.x()
- st::gifsSearchCancelPosition.x()
- st::gifsSearchCancel.width;
_field->resizeToWidth(fieldWidth);
_field->moveToLeft(st::gifsSearchFieldPosition.x(), st::gifsSearchFieldPosition.y());
_cancel->moveToRight(st::gifsSearchCancelPosition.x(), st::gifsSearchCancelPosition.y());
}
void GifsListWidget::Footer::processPanelHideFinished() {
// Preserve panel state through visibility toggles.
//_field->setText(QString());
}
GifsListWidget::GifsListWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
@ -241,8 +154,89 @@ auto GifsListWidget::inlineResultChosen() const
object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() {
Expects(_footer == nullptr);
auto result = object_ptr<Footer>(this);
using FooterDescriptor = StickersListFooter::Descriptor;
auto result = object_ptr<StickersListFooter>(FooterDescriptor{
.session = &session(),
.paused = pausedMethod(),
.parent = this,
.st = &st(),
});
_footer = result;
GifSectionsValue(
&session()
) | rpl::start_with_next([=](std::vector<GifSection> &&list) {
_sections = std::move(list);
refreshIcons();
}, _footer->lifetime());
_footer->setChosen(
) | rpl::start_with_next([=](uint64 setId) {
_chosenSetId = setId;
refreshIcons();
const auto i = ranges::find(_sections, setId, [](GifSection value) {
return value.document->id;
});
if (i != end(_sections)) {
searchForGifs(i->emoji->text());
}
}, _footer->lifetime());
return result;
}
void GifsListWidget::refreshIcons() {
if (_footer) {
_footer->refreshIcons(
fillIcons(),
_chosenSetId,
nullptr,
ValidateIconAnimations::None);
}
}
std::vector<StickerIcon> GifsListWidget::fillIcons() {
auto result = std::vector<StickerIcon>();
result.reserve(_sections.size() + 1);
result.emplace_back(Data::Stickers::RecentSetId);
for (const auto &section : _sections) {
const auto s = section.document;
const auto id = s->id;
const auto availw = st::stickerIconWidth - 2 * st::stickerIconPadding;
const auto availh = st().footer - 2 * st::stickerIconPadding;
const auto size = 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;
const auto owner = &s->owner();
const auto already = _fakeSets.find(id);
const auto set = (already != end(_fakeSets))
? already
: _fakeSets.emplace(
id,
std::make_unique<Data::StickersSet>(
owner,
id,
0,
0,
QString(),
QString(),
0,
Data::StickersSetFlag::Special,
0)).first;
result.emplace_back(set->second.get(), s, pixw, pixh);
}
return result;
}
@ -546,10 +540,16 @@ TabbedSelector::InnerFooter *GifsListWidget::getFooter() const {
void GifsListWidget::processHideFinished() {
clearSelection();
clearHeavyData();
if (_footer) {
_footer->clearHeavyData();
}
}
void GifsListWidget::processPanelHideFinished() {
clearHeavyData();
if (_footer) {
_footer->clearHeavyData();
}
}
void GifsListWidget::clearHeavyData() {
@ -813,6 +813,8 @@ void GifsListWidget::setupSearch() {
});
_search->queryValue(
) | rpl::start_with_next([=](std::vector<QString> &&query) {
_chosenSetId = Data::Stickers::RecentSetId;
refreshIcons();
searchForGifs(ranges::accumulate(query, QString(), [](
QString a,
QString b) {

View file

@ -39,6 +39,10 @@ namespace SendMenu {
enum class Type;
} // namespace SendMenu
namespace Data {
class StickersSet;
} // namespace Data
namespace ChatHelpers {
void AddGifAction(
@ -46,6 +50,10 @@ void AddGifAction(
Window::SessionController *controller,
not_null<DocumentData*> document);
class StickersListFooter;
struct StickerIcon;
struct GifSection;
class GifsListWidget
: public TabbedSelector::Inner
, public InlineBots::Layout::Context {
@ -109,7 +117,6 @@ private:
Inlines,
Gifs,
};
class Footer;
using InlineResult = InlineBots::Result;
using InlineResults = std::vector<std::unique_ptr<InlineResult>>;
@ -134,6 +141,8 @@ private:
void updateSelected();
void paintInlineItems(Painter &p, QRect clip);
void refreshIcons();
[[nodiscard]] std::vector<StickerIcon> fillIcons();
void updateInlineItems();
void repaintItems(crl::time now = 0);
@ -171,7 +180,10 @@ private:
not_null<InlineResult*>,
std::unique_ptr<LayoutItem>> _inlineLayouts;
Footer *_footer = nullptr;
StickersListFooter *_footer = nullptr;
std::vector<GifSection> _sections;
base::flat_map<uint64, std::unique_ptr<Data::StickersSet>> _fakeSets;
uint64 _chosenSetId = 0;
Mosaic::Layout::MosaicLayout<LayoutItem> _mosaic;

View file

@ -384,6 +384,7 @@ void EmojiPack::applySet(const MTPDmessages_stickerSet &data) {
for (const auto &[emoji, document] : was) {
refreshItems(emoji);
}
_refreshed.fire({});
}
void EmojiPack::applyAnimationsSet(const MTPDmessages_stickerSet &data) {

View file

@ -87,6 +87,9 @@ public:
[[nodiscard]] int animationsVersion() const {
return _animationsVersion;
}
[[nodiscard]] rpl::producer<> refreshed() const {
return _refreshed.events();
}
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> effectPlayer(
not_null<DocumentData*> document,
@ -135,6 +138,8 @@ private:
not_null<DocumentData*>,
std::weak_ptr<Lottie::FrameProvider>> _sharedProviders;
rpl::event_stream<> _refreshed;
rpl::lifetime _lifetime;
};

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "chat_helpers/stickers_list_footer.h"
#include "chat_helpers/stickers_emoji_pack.h"
#include "chat_helpers/stickers_lottie.h"
#include "data/stickers/data_stickers_set.h"
#include "data/stickers/data_stickers.h"
@ -16,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_single_player.h"
@ -90,6 +93,58 @@ std::optional<EmojiSection> SetIdEmojiSection(uint64 id) {
: std::optional<EmojiSection>();
}
[[nodiscard]] std::vector<QString> GifSearchEmojiFallback() {
return {
u"\xf0\x9f\x91\x8d"_q,
u"\xf0\x9f\x98\x98"_q,
u"\xf0\x9f\x98\x8d"_q,
u"\xf0\x9f\x98\xa1"_q,
u"\xf0\x9f\xa5\xb3"_q,
u"\xf0\x9f\x98\x82"_q,
u"\xf0\x9f\x98\xae"_q,
u"\xf0\x9f\x99\x84"_q,
u"\xf0\x9f\x98\x8e"_q,
u"\xf0\x9f\x91\x8e"_q,
};
}
rpl::producer<std::vector<GifSection>> GifSectionsValue(
not_null<Main::Session*> session) {
const auto config = &session->account().appConfig();
return rpl::single(
rpl::empty_value()
) | rpl::then(
config->refreshed()
) | rpl::map([=] {
return config->get<std::vector<QString>>(
u"gif_search_emojies"_q,
GifSearchEmojiFallback());
}) | rpl::distinct_until_changed(
) | rpl::map([=](const std::vector<QString> &emoji) {
const auto list = ranges::views::all(
emoji
) | ranges::views::transform([](const QString &val) {
return Ui::Emoji::Find(val);
}) | ranges::views::filter([](EmojiPtr emoji) {
return emoji != nullptr;
}) | ranges::to_vector;
const auto pack = &session->emojiStickersPack();
return rpl::single(
rpl::empty_value()
) | rpl::then(
pack->refreshed()
) | rpl::map([=, list = std::move(list)] {
return list | ranges::views::transform([&](EmojiPtr emoji) {
const auto document = pack->stickerForEmoji(emoji).document;
return GifSection{ document, emoji };
}) | ranges::views::filter([](GifSection section) {
return (section.document != nullptr);
}) | ranges::to_vector;
}) | rpl::distinct_until_changed();
}) | rpl::flatten_latest();
}
StickerIcon::StickerIcon(uint64 setId) : setId(setId) {
}

View file

@ -52,6 +52,17 @@ enum class ValidateIconAnimations {
[[nodiscard]] uint64 SearchEmojiSectionSetId();
[[nodiscard]] std::optional<Ui::Emoji::Section> SetIdEmojiSection(uint64 id);
struct GifSection {
DocumentData *document = nullptr;
EmojiPtr emoji;
friend inline constexpr auto operator<=>(
GifSection,
GifSection) = default;
};
[[nodiscard]] rpl::producer<std::vector<GifSection>> GifSectionsValue(
not_null<Main::Session*> session);
struct StickerIcon {
explicit StickerIcon(uint64 setId);
StickerIcon(