mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Use StickersListFooter for GIFs section.
This commit is contained in:
parent
d51d1939b0
commit
ffb2c5093d
7 changed files with 177 additions and 93 deletions
|
@ -353,8 +353,6 @@ gifsSearchField: defaultMultiSelectSearchField;
|
||||||
gifsSearchFieldPosition: point(42px, 7px);
|
gifsSearchFieldPosition: point(42px, 7px);
|
||||||
gifsSearchCancel: defaultMultiSelectSearchCancel;
|
gifsSearchCancel: defaultMultiSelectSearchCancel;
|
||||||
gifsSearchCancelPosition: point(1px, 1px);
|
gifsSearchCancelPosition: point(1px, 1px);
|
||||||
gifsSearchIcon: boxFieldSearchIcon;
|
|
||||||
gifsSearchIconPosition: point(6px, 7px);
|
|
||||||
|
|
||||||
emojiSuggestionsDropdown: InnerDropdown(defaultInnerDropdown) {
|
emojiSuggestionsDropdown: InnerDropdown(defaultInnerDropdown) {
|
||||||
scrollMargin: margins(0px, emojiColorsPadding, 0px, emojiColorsPadding);
|
scrollMargin: margins(0px, emojiColorsPadding, 0px, emojiColorsPadding);
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_toggling_media.h" // Api::ToggleSavedGif
|
#include "api/api_toggling_media.h" // Api::ToggleSavedGif
|
||||||
#include "base/const_string.h"
|
#include "base/const_string.h"
|
||||||
#include "base/qt/qt_key_modifiers.h"
|
#include "base/qt/qt_key_modifiers.h"
|
||||||
|
#include "chat_helpers/stickers_list_footer.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_emoji_statuses.h"
|
#include "data/data_emoji_statuses.h"
|
||||||
|
@ -83,94 +84,6 @@ void AddGifAction(
|
||||||
}, saved ? &st::menuIconDelete : &st::menuIconGif);
|
}, 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(
|
GifsListWidget::GifsListWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
|
@ -241,8 +154,89 @@ auto GifsListWidget::inlineResultChosen() const
|
||||||
object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() {
|
object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() {
|
||||||
Expects(_footer == nullptr);
|
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;
|
_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 §ion : _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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,10 +540,16 @@ TabbedSelector::InnerFooter *GifsListWidget::getFooter() const {
|
||||||
void GifsListWidget::processHideFinished() {
|
void GifsListWidget::processHideFinished() {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
clearHeavyData();
|
clearHeavyData();
|
||||||
|
if (_footer) {
|
||||||
|
_footer->clearHeavyData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GifsListWidget::processPanelHideFinished() {
|
void GifsListWidget::processPanelHideFinished() {
|
||||||
clearHeavyData();
|
clearHeavyData();
|
||||||
|
if (_footer) {
|
||||||
|
_footer->clearHeavyData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GifsListWidget::clearHeavyData() {
|
void GifsListWidget::clearHeavyData() {
|
||||||
|
@ -813,6 +813,8 @@ void GifsListWidget::setupSearch() {
|
||||||
});
|
});
|
||||||
_search->queryValue(
|
_search->queryValue(
|
||||||
) | rpl::start_with_next([=](std::vector<QString> &&query) {
|
) | rpl::start_with_next([=](std::vector<QString> &&query) {
|
||||||
|
_chosenSetId = Data::Stickers::RecentSetId;
|
||||||
|
refreshIcons();
|
||||||
searchForGifs(ranges::accumulate(query, QString(), [](
|
searchForGifs(ranges::accumulate(query, QString(), [](
|
||||||
QString a,
|
QString a,
|
||||||
QString b) {
|
QString b) {
|
||||||
|
|
|
@ -39,6 +39,10 @@ namespace SendMenu {
|
||||||
enum class Type;
|
enum class Type;
|
||||||
} // namespace SendMenu
|
} // namespace SendMenu
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class StickersSet;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
|
|
||||||
void AddGifAction(
|
void AddGifAction(
|
||||||
|
@ -46,6 +50,10 @@ void AddGifAction(
|
||||||
Window::SessionController *controller,
|
Window::SessionController *controller,
|
||||||
not_null<DocumentData*> document);
|
not_null<DocumentData*> document);
|
||||||
|
|
||||||
|
class StickersListFooter;
|
||||||
|
struct StickerIcon;
|
||||||
|
struct GifSection;
|
||||||
|
|
||||||
class GifsListWidget
|
class GifsListWidget
|
||||||
: public TabbedSelector::Inner
|
: public TabbedSelector::Inner
|
||||||
, public InlineBots::Layout::Context {
|
, public InlineBots::Layout::Context {
|
||||||
|
@ -109,7 +117,6 @@ private:
|
||||||
Inlines,
|
Inlines,
|
||||||
Gifs,
|
Gifs,
|
||||||
};
|
};
|
||||||
class Footer;
|
|
||||||
|
|
||||||
using InlineResult = InlineBots::Result;
|
using InlineResult = InlineBots::Result;
|
||||||
using InlineResults = std::vector<std::unique_ptr<InlineResult>>;
|
using InlineResults = std::vector<std::unique_ptr<InlineResult>>;
|
||||||
|
@ -134,6 +141,8 @@ private:
|
||||||
|
|
||||||
void updateSelected();
|
void updateSelected();
|
||||||
void paintInlineItems(Painter &p, QRect clip);
|
void paintInlineItems(Painter &p, QRect clip);
|
||||||
|
void refreshIcons();
|
||||||
|
[[nodiscard]] std::vector<StickerIcon> fillIcons();
|
||||||
|
|
||||||
void updateInlineItems();
|
void updateInlineItems();
|
||||||
void repaintItems(crl::time now = 0);
|
void repaintItems(crl::time now = 0);
|
||||||
|
@ -171,7 +180,10 @@ private:
|
||||||
not_null<InlineResult*>,
|
not_null<InlineResult*>,
|
||||||
std::unique_ptr<LayoutItem>> _inlineLayouts;
|
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;
|
Mosaic::Layout::MosaicLayout<LayoutItem> _mosaic;
|
||||||
|
|
||||||
|
|
|
@ -384,6 +384,7 @@ void EmojiPack::applySet(const MTPDmessages_stickerSet &data) {
|
||||||
for (const auto &[emoji, document] : was) {
|
for (const auto &[emoji, document] : was) {
|
||||||
refreshItems(emoji);
|
refreshItems(emoji);
|
||||||
}
|
}
|
||||||
|
_refreshed.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiPack::applyAnimationsSet(const MTPDmessages_stickerSet &data) {
|
void EmojiPack::applyAnimationsSet(const MTPDmessages_stickerSet &data) {
|
||||||
|
|
|
@ -87,6 +87,9 @@ public:
|
||||||
[[nodiscard]] int animationsVersion() const {
|
[[nodiscard]] int animationsVersion() const {
|
||||||
return _animationsVersion;
|
return _animationsVersion;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] rpl::producer<> refreshed() const {
|
||||||
|
return _refreshed.events();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> effectPlayer(
|
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> effectPlayer(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
|
@ -135,6 +138,8 @@ private:
|
||||||
not_null<DocumentData*>,
|
not_null<DocumentData*>,
|
||||||
std::weak_ptr<Lottie::FrameProvider>> _sharedProviders;
|
std::weak_ptr<Lottie::FrameProvider>> _sharedProviders;
|
||||||
|
|
||||||
|
rpl::event_stream<> _refreshed;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "chat_helpers/stickers_list_footer.h"
|
#include "chat_helpers/stickers_list_footer.h"
|
||||||
|
|
||||||
|
#include "chat_helpers/stickers_emoji_pack.h"
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "data/stickers/data_stickers_set.h"
|
#include "data/stickers/data_stickers_set.h"
|
||||||
#include "data/stickers/data_stickers.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_session.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
|
#include "main/main_account.h"
|
||||||
|
#include "main/main_app_config.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "lottie/lottie_single_player.h"
|
#include "lottie/lottie_single_player.h"
|
||||||
|
@ -90,6 +93,58 @@ std::optional<EmojiSection> SetIdEmojiSection(uint64 id) {
|
||||||
: std::optional<EmojiSection>();
|
: 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) {
|
StickerIcon::StickerIcon(uint64 setId) : setId(setId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,17 @@ enum class ValidateIconAnimations {
|
||||||
[[nodiscard]] uint64 SearchEmojiSectionSetId();
|
[[nodiscard]] uint64 SearchEmojiSectionSetId();
|
||||||
[[nodiscard]] std::optional<Ui::Emoji::Section> SetIdEmojiSection(uint64 id);
|
[[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 {
|
struct StickerIcon {
|
||||||
explicit StickerIcon(uint64 setId);
|
explicit StickerIcon(uint64 setId);
|
||||||
StickerIcon(
|
StickerIcon(
|
||||||
|
|
Loading…
Add table
Reference in a new issue