mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Support suggestions of custom emoji.
This commit is contained in:
parent
bf286cf175
commit
04d4fdbf9b
5 changed files with 271 additions and 143 deletions
|
@ -38,81 +38,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace ChatHelpers {
|
||||
namespace {
|
||||
|
||||
constexpr auto kFakeEmojiDocumentIdBase = 0x7777'FFFF'FFFF'0000ULL;
|
||||
|
||||
using Core::RecentEmojiId;
|
||||
using Core::RecentEmojiDocument;
|
||||
|
||||
[[nodiscard]] DocumentId FakeEmojiDocumentId(EmojiPtr emoji) {
|
||||
return kFakeEmojiDocumentIdBase + emoji->index();
|
||||
}
|
||||
|
||||
class DefaultEmojiLoader final : public Ui::CustomEmoji::Loader {
|
||||
public:
|
||||
DefaultEmojiLoader(EmojiPtr emoji, int size);
|
||||
|
||||
QString entityData() override;
|
||||
|
||||
void load(Fn<void(LoadResult)> loaded) override;
|
||||
bool loading() override;
|
||||
void cancel() override;
|
||||
Ui::CustomEmoji::Preview preview() override;
|
||||
|
||||
private:
|
||||
void validateImage();
|
||||
|
||||
EmojiPtr _emoji = nullptr;
|
||||
QImage _image;
|
||||
int _size = 0;
|
||||
|
||||
};
|
||||
|
||||
DefaultEmojiLoader::DefaultEmojiLoader(EmojiPtr emoji, int size)
|
||||
: _emoji(emoji)
|
||||
, _size(size) {
|
||||
}
|
||||
|
||||
void DefaultEmojiLoader::load(Fn<void(LoadResult)> loaded) {
|
||||
validateImage();
|
||||
const auto data = entityData();
|
||||
const auto unloader = [emoji = _emoji, size = _size] {
|
||||
return std::make_unique<DefaultEmojiLoader>(emoji, size);
|
||||
};
|
||||
auto cache = Ui::CustomEmoji::Cache(_size);
|
||||
cache.add(0, _image);
|
||||
cache.finish();
|
||||
loaded(Ui::CustomEmoji::Cached(data, unloader, std::move(cache)));
|
||||
}
|
||||
|
||||
void DefaultEmojiLoader::validateImage() {
|
||||
if (!_image.isNull()) {
|
||||
return;
|
||||
}
|
||||
_image = QImage(
|
||||
{ _size, _size },
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_image.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
_image.fill(Qt::transparent);
|
||||
QPainter p(&_image);
|
||||
Ui::Emoji::Draw(p, _emoji, _size, 0, 0);
|
||||
}
|
||||
|
||||
QString DefaultEmojiLoader::entityData() {
|
||||
return "default-emoji://" + _emoji->id();
|
||||
}
|
||||
|
||||
bool DefaultEmojiLoader::loading() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DefaultEmojiLoader::cancel() {
|
||||
}
|
||||
|
||||
Ui::CustomEmoji::Preview DefaultEmojiLoader::preview() {
|
||||
validateImage();
|
||||
return { _image };
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class EmojiColorPicker : public Ui::RpWidget {
|
||||
|
@ -181,7 +109,7 @@ struct EmojiListWidget::CustomInstance {
|
|||
};
|
||||
|
||||
struct EmojiListWidget::RecentOne {
|
||||
not_null<CustomInstance*> instance;
|
||||
CustomInstance *instance = nullptr;
|
||||
RecentEmojiId id;
|
||||
};
|
||||
|
||||
|
@ -881,7 +809,7 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
|
|||
if (info.section == int(Section::Recent)) {
|
||||
drawRecent(p, w, now, paused, index);
|
||||
} else if (info.section < kEmojiSectionCount) {
|
||||
drawEmoji(p, w, info.section, index);
|
||||
drawEmoji(p, w, _emoji[info.section][index]);
|
||||
} else {
|
||||
const auto set = info.section - kEmojiSectionCount;
|
||||
drawCustom(p, w, now, paused, set, index);
|
||||
|
@ -919,24 +847,29 @@ void EmojiListWidget::drawRecent(
|
|||
int index) {
|
||||
const auto size = (_esize / cIntRetinaFactor());
|
||||
_recentPainted = true;
|
||||
_recent[index].instance->object.paint(
|
||||
p,
|
||||
position.x() + (_singleSize.width() - size) / 2,
|
||||
position.y() + (_singleSize.height() - size) / 2,
|
||||
now,
|
||||
st::windowBgRipple->c,
|
||||
paused);
|
||||
if (const auto emoji = std::get_if<EmojiPtr>(&_recent[index].id.data)) {
|
||||
drawEmoji(p, position, *emoji);
|
||||
} else {
|
||||
Assert(_recent[index].instance != nullptr);
|
||||
|
||||
_recent[index].instance->object.paint(
|
||||
p,
|
||||
position.x() + (_singleSize.width() - size) / 2,
|
||||
position.y() + (_singleSize.height() - size) / 2,
|
||||
now,
|
||||
st::windowBgRipple->c,
|
||||
paused);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiListWidget::drawEmoji(
|
||||
QPainter &p,
|
||||
QPoint position,
|
||||
int section,
|
||||
int index) {
|
||||
EmojiPtr emoji) {
|
||||
const auto size = (_esize / cIntRetinaFactor());
|
||||
Ui::Emoji::Draw(
|
||||
p,
|
||||
_emoji[section][index],
|
||||
emoji,
|
||||
_esize,
|
||||
position.x() + (_singleSize.width() - size) / 2,
|
||||
position.y() + (_singleSize.height() - size) / 2);
|
||||
|
@ -1385,7 +1318,7 @@ auto EmojiListWidget::resolveCustomInstance(
|
|||
setId);
|
||||
if (recentOnly) {
|
||||
for (auto &recent : _recent) {
|
||||
if (recent.instance == i->second.get()) {
|
||||
if (recent.instance && recent.instance == i->second.get()) {
|
||||
recent.instance = instance.get();
|
||||
}
|
||||
}
|
||||
|
@ -1399,35 +1332,16 @@ auto EmojiListWidget::resolveCustomInstance(
|
|||
|
||||
auto EmojiListWidget::resolveCustomInstance(
|
||||
RecentEmojiId customId)
|
||||
-> not_null<CustomInstance*> {
|
||||
-> CustomInstance* {
|
||||
const auto &data = customId.data;
|
||||
if (const auto document = std::get_if<RecentEmojiDocument>(&data)) {
|
||||
return resolveCustomInstance(document->id);
|
||||
} else if (const auto emoji = std::get_if<EmojiPtr>(&data)) {
|
||||
return resolveCustomInstance(FakeEmojiDocumentId(*emoji), *emoji);
|
||||
return nullptr;
|
||||
}
|
||||
Unexpected("Custom recent emoji id.");
|
||||
}
|
||||
|
||||
auto EmojiListWidget::resolveCustomInstance(
|
||||
DocumentId fakeId,
|
||||
EmojiPtr emoji)
|
||||
-> not_null<CustomInstance*> {
|
||||
const auto i = _instances.find(fakeId);
|
||||
if (i != end(_instances)) {
|
||||
return i->second.get();
|
||||
}
|
||||
return _instances.emplace(
|
||||
fakeId,
|
||||
std::make_unique<CustomInstance>(
|
||||
std::make_unique<DefaultEmojiLoader>(
|
||||
emoji,
|
||||
Ui::Emoji::GetSizeLarge()),
|
||||
[](const auto&, const auto&) {},
|
||||
[] {},
|
||||
true)).first->second.get();
|
||||
}
|
||||
|
||||
auto EmojiListWidget::resolveCustomInstance(
|
||||
DocumentId documentId)
|
||||
-> not_null<CustomInstance*> {
|
||||
|
|
|
@ -196,8 +196,7 @@ private:
|
|||
void drawEmoji(
|
||||
QPainter &p,
|
||||
QPoint position,
|
||||
int section,
|
||||
int index);
|
||||
EmojiPtr emoji);
|
||||
void drawCustom(
|
||||
QPainter &p,
|
||||
QPoint position,
|
||||
|
@ -234,11 +233,8 @@ private:
|
|||
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
|
||||
not_null<DocumentData*> document,
|
||||
uint64 setId);
|
||||
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
|
||||
[[nodiscard]] CustomInstance *resolveCustomInstance(
|
||||
Core::RecentEmojiId customId);
|
||||
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
|
||||
DocumentId fakeId,
|
||||
EmojiPtr emoji);
|
||||
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
|
||||
DocumentId documentId);
|
||||
[[nodiscard]] std::unique_ptr<CustomInstance> customInstanceWithLoader(
|
||||
|
|
|
@ -22,6 +22,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/application.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
@ -37,8 +41,36 @@ constexpr auto kAnimationDuration = crl::time(120);
|
|||
|
||||
} // namespace
|
||||
|
||||
SuggestionsWidget::SuggestionsWidget(QWidget *parent)
|
||||
struct SuggestionsWidget::CustomInstance {
|
||||
CustomInstance(
|
||||
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
|
||||
Fn<void(
|
||||
not_null<Ui::CustomEmoji::Instance*>,
|
||||
Ui::CustomEmoji::RepaintRequest)> repaintLater,
|
||||
Fn<void()> repaint);
|
||||
|
||||
Ui::CustomEmoji::Instance emoji;
|
||||
Ui::CustomEmoji::Object object;
|
||||
};
|
||||
|
||||
SuggestionsWidget::CustomInstance::CustomInstance(
|
||||
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
|
||||
Fn<void(
|
||||
not_null<Ui::CustomEmoji::Instance*>,
|
||||
Ui::CustomEmoji::RepaintRequest)> repaintLater,
|
||||
Fn<void()> repaint)
|
||||
: emoji(
|
||||
Ui::CustomEmoji::Loading(std::move(loader), Ui::CustomEmoji::Preview()),
|
||||
std::move(repaintLater))
|
||||
, object(&emoji, std::move(repaint)) {
|
||||
}
|
||||
|
||||
SuggestionsWidget::SuggestionsWidget(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session)
|
||||
: RpWidget(parent)
|
||||
, _session(session)
|
||||
, _repaintTimer([=] { invokeRepaints(); })
|
||||
, _oneWidth(st::emojiSuggestionSize)
|
||||
, _padding(st::emojiSuggestionsPadding) {
|
||||
resize(
|
||||
|
@ -47,11 +79,13 @@ SuggestionsWidget::SuggestionsWidget(QWidget *parent)
|
|||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
SuggestionsWidget::~SuggestionsWidget() = default;
|
||||
|
||||
rpl::producer<bool> SuggestionsWidget::toggleAnimated() const {
|
||||
return _toggleAnimated.events();
|
||||
}
|
||||
|
||||
rpl::producer<QString> SuggestionsWidget::triggered() const {
|
||||
auto SuggestionsWidget::triggered() const -> rpl::producer<Chosen> {
|
||||
return _triggered.events();
|
||||
}
|
||||
|
||||
|
@ -60,7 +94,7 @@ void SuggestionsWidget::showWithQuery(const QString &query, bool force) {
|
|||
return;
|
||||
}
|
||||
_query = query;
|
||||
auto rows = getRowsByQuery();
|
||||
auto rows = prependCustom(getRowsByQuery());
|
||||
if (rows.empty()) {
|
||||
_toggleAnimated.fire(false);
|
||||
}
|
||||
|
@ -83,6 +117,142 @@ void SuggestionsWidget::selectFirstResult() {
|
|||
}
|
||||
}
|
||||
|
||||
auto SuggestionsWidget::prependCustom(std::vector<Row> rows)
|
||||
-> std::vector<Row> {
|
||||
if (rows.empty()) {
|
||||
return {};
|
||||
}
|
||||
struct Custom {
|
||||
not_null<DocumentData*> document;
|
||||
not_null<EmojiPtr> emoji;
|
||||
QString replacement;
|
||||
};
|
||||
auto custom = base::flat_multi_map<int, Custom>();
|
||||
const auto premium = _session->premium();
|
||||
const auto stickers = &_session->data().stickers();
|
||||
for (const auto setId : stickers->emojiSetsOrder()) {
|
||||
const auto i = stickers->sets().find(setId);
|
||||
if (i == end(stickers->sets())) {
|
||||
continue;
|
||||
}
|
||||
for (const auto &document : i->second->stickers) {
|
||||
if (!premium && document->isPremiumEmoji()) {
|
||||
// Skip the whole premium emoji set.
|
||||
break;
|
||||
}
|
||||
if (const auto sticker = document->sticker()) {
|
||||
if (const auto emoji = Ui::Emoji::Find(sticker->alt)) {
|
||||
const auto j = ranges::find(
|
||||
rows,
|
||||
not_null{ emoji },
|
||||
&Row::emoji);
|
||||
if (j != end(rows)) {
|
||||
custom.emplace(int(j - begin(rows)), Custom{
|
||||
.document = document,
|
||||
.emoji = j->emoji,
|
||||
.replacement = j->replacement,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (custom.empty()) {
|
||||
return rows;
|
||||
}
|
||||
auto result = std::vector<Row>();
|
||||
result.reserve(custom.size() + rows.size());
|
||||
for (const auto &[position, one] : custom) {
|
||||
result.push_back(Row(one.emoji, one.replacement));
|
||||
result.back().document = one.document;
|
||||
result.back().instance = resolveCustomInstance(one.document);
|
||||
}
|
||||
for (auto &row : rows) {
|
||||
result.push_back(std::move(row));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto SuggestionsWidget::resolveCustomInstance(
|
||||
not_null<DocumentData*> document)
|
||||
-> not_null<CustomInstance*> {
|
||||
const auto i = _instances.find(document);
|
||||
if (i != end(_instances)) {
|
||||
return i->second.get();
|
||||
}
|
||||
const auto repaintDelayed = [=](
|
||||
not_null<Ui::CustomEmoji::Instance*> instance,
|
||||
Ui::CustomEmoji::RepaintRequest request) {
|
||||
if (_instances.empty() || !request.when) {
|
||||
return;
|
||||
}
|
||||
auto &when = _repaints[request.duration];
|
||||
if (when < request.when) {
|
||||
when = request.when;
|
||||
}
|
||||
if (_repaintTimerScheduled) {
|
||||
return;
|
||||
}
|
||||
scheduleRepaintTimer();
|
||||
};
|
||||
const auto repaintNow = [=] {
|
||||
update();
|
||||
};
|
||||
auto instance = std::make_unique<CustomInstance>(
|
||||
_session->data().customEmojiManager().createLoader(
|
||||
document,
|
||||
Data::CustomEmojiManager::SizeTag::Large),
|
||||
std::move(repaintDelayed),
|
||||
std::move(repaintNow));
|
||||
return _instances.emplace(
|
||||
document,
|
||||
std::move(instance)
|
||||
).first->second.get();
|
||||
}
|
||||
|
||||
void SuggestionsWidget::scheduleRepaintTimer() {
|
||||
_repaintTimerScheduled = true;
|
||||
Ui::PostponeCall(this, [=] {
|
||||
_repaintTimerScheduled = false;
|
||||
|
||||
auto next = crl::time();
|
||||
for (const auto &[duration, when] : _repaints) {
|
||||
if (!next || next > when) {
|
||||
next = when;
|
||||
}
|
||||
}
|
||||
if (next && (!_repaintNext || _repaintNext > next)) {
|
||||
const auto now = crl::now();
|
||||
if (now >= next) {
|
||||
_repaintNext = 0;
|
||||
_repaintTimer.cancel();
|
||||
invokeRepaints();
|
||||
} else {
|
||||
_repaintNext = next;
|
||||
_repaintTimer.callOnce(next - now);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SuggestionsWidget::invokeRepaints() {
|
||||
_repaintNext = 0;
|
||||
auto invoke = false;
|
||||
const auto now = crl::now();
|
||||
for (auto i = begin(_repaints); i != end(_repaints);) {
|
||||
if (i->second > now) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
invoke = true;
|
||||
i = _repaints.erase(i);
|
||||
}
|
||||
if (invoke) {
|
||||
update();
|
||||
}
|
||||
scheduleRepaintTimer();
|
||||
}
|
||||
|
||||
SuggestionsWidget::Row::Row(
|
||||
not_null<EmojiPtr> emoji,
|
||||
const QString &replacement)
|
||||
|
@ -230,18 +400,20 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) {
|
|||
Ui::StickerHoverCorners);
|
||||
}
|
||||
|
||||
const auto now = crl::now();
|
||||
const auto preview = st::windowBgOver->c;
|
||||
for (auto i = from; i != till; ++i) {
|
||||
const auto &row = _rows[i];
|
||||
const auto emoji = row.emoji;
|
||||
const auto esize = Ui::Emoji::GetSizeLarge();
|
||||
const auto x = i * _oneWidth;
|
||||
const auto y = 0;
|
||||
Ui::Emoji::Draw(
|
||||
p,
|
||||
emoji,
|
||||
esize,
|
||||
x + (_oneWidth - (esize / cIntRetinaFactor())) / 2,
|
||||
y + (_oneWidth - (esize / cIntRetinaFactor())) / 2);
|
||||
const auto size = esize / style::DevicePixelRatio();
|
||||
const auto x = i * _oneWidth + (_oneWidth - size) / 2;
|
||||
const auto y = (_oneWidth - size) / 2;
|
||||
if (row.instance) {
|
||||
row.instance->object.paint(p, x, y, now, preview, false);
|
||||
} else {
|
||||
Ui::Emoji::Draw(p, emoji, esize, x, y);
|
||||
}
|
||||
}
|
||||
paintFadings(p);
|
||||
}
|
||||
|
@ -496,7 +668,10 @@ bool SuggestionsWidget::triggerSelectedRow() const {
|
|||
}
|
||||
|
||||
void SuggestionsWidget::triggerRow(const Row &row) const {
|
||||
_triggered.fire(row.emoji->text());
|
||||
_triggered.fire({
|
||||
row.emoji->text(),
|
||||
row.document ? Data::SerializeCustomEmojiId(row.document) : QString()
|
||||
});
|
||||
}
|
||||
|
||||
void SuggestionsWidget::enterEventHook(QEnterEvent *e) {
|
||||
|
@ -525,7 +700,9 @@ SuggestionsController::SuggestionsController(
|
|||
st::emojiSuggestionsDropdown);
|
||||
_container->setAutoHiding(false);
|
||||
_suggestions = _container->setOwnedWidget(
|
||||
object_ptr<Ui::Emoji::SuggestionsWidget>(_container));
|
||||
object_ptr<Ui::Emoji::SuggestionsWidget>(
|
||||
_container,
|
||||
session));
|
||||
|
||||
setReplaceCallback(nullptr);
|
||||
|
||||
|
@ -559,8 +736,8 @@ SuggestionsController::SuggestionsController(
|
|||
suggestionsUpdated(visible);
|
||||
}, _lifetime);
|
||||
_suggestions->triggered(
|
||||
) | rpl::start_with_next([=](QString replacement) {
|
||||
replaceCurrent(replacement);
|
||||
) | rpl::start_with_next([=](const SuggestionsWidget::Chosen &chosen) {
|
||||
replaceCurrent(chosen.emoji, chosen.customData);
|
||||
}, _lifetime);
|
||||
Core::App().emojiKeywords().refreshed(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
@ -589,8 +766,13 @@ SuggestionsController *SuggestionsController::Init(
|
|||
result->setReplaceCallback([=](
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement) {
|
||||
field->commitInstantReplacement(from, till, replacement);
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData) {
|
||||
field->commitInstantReplacement(
|
||||
from,
|
||||
till,
|
||||
replacement,
|
||||
customEmojiData);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
@ -599,11 +781,16 @@ void SuggestionsController::setReplaceCallback(
|
|||
Fn<void(
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement)> callback) {
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData)> callback) {
|
||||
if (callback) {
|
||||
_replaceCallback = std::move(callback);
|
||||
} else {
|
||||
_replaceCallback = [=](int from, int till, const QString &replacement) {
|
||||
_replaceCallback = [=](
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData) {
|
||||
auto cursor = _field->textCursor();
|
||||
cursor.setPosition(from);
|
||||
cursor.setPosition(till, QTextCursor::KeepAnchor);
|
||||
|
@ -667,7 +854,9 @@ QString SuggestionsController::getEmojiQuery() {
|
|||
if (from >= position || till < position) {
|
||||
continue;
|
||||
}
|
||||
if (fragment.charFormat().isImageFormat()) {
|
||||
const auto format = fragment.charFormat();
|
||||
if (format.isImageFormat()
|
||||
|| format.objectType() == InputField::kCustomEmojiFormat) {
|
||||
continue;
|
||||
}
|
||||
_queryStartPosition = from;
|
||||
|
@ -714,7 +903,9 @@ QString SuggestionsController::getEmojiQuery() {
|
|||
return text;
|
||||
}
|
||||
|
||||
void SuggestionsController::replaceCurrent(const QString &replacement) {
|
||||
void SuggestionsController::replaceCurrent(
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData) {
|
||||
const auto suggestion = getEmojiQuery();
|
||||
if (suggestion.isEmpty()) {
|
||||
showWithQuery(QString());
|
||||
|
@ -722,7 +913,7 @@ void SuggestionsController::replaceCurrent(const QString &replacement) {
|
|||
const auto cursor = _field->textCursor();
|
||||
const auto position = cursor.position();
|
||||
const auto from = position - suggestion.size();
|
||||
_replaceCallback(from, position, replacement);
|
||||
_replaceCallback(from, position, replacement, customEmojiData);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,19 +27,28 @@ namespace Emoji {
|
|||
|
||||
class SuggestionsWidget final : public Ui::RpWidget {
|
||||
public:
|
||||
SuggestionsWidget(QWidget *parent);
|
||||
SuggestionsWidget(QWidget *parent, not_null<Main::Session*> session);
|
||||
~SuggestionsWidget();
|
||||
|
||||
void showWithQuery(const QString &query, bool force = false);
|
||||
void selectFirstResult();
|
||||
bool handleKeyEvent(int key);
|
||||
|
||||
rpl::producer<bool> toggleAnimated() const;
|
||||
rpl::producer<QString> triggered() const;
|
||||
[[nodiscard]] rpl::producer<bool> toggleAnimated() const;
|
||||
|
||||
struct Chosen {
|
||||
QString emoji;
|
||||
QString customData;
|
||||
};
|
||||
[[nodiscard]] rpl::producer<Chosen> triggered() const;
|
||||
|
||||
private:
|
||||
struct CustomInstance;
|
||||
struct Row {
|
||||
Row(not_null<EmojiPtr> emoji, const QString &replacement);
|
||||
|
||||
CustomInstance *instance = nullptr;
|
||||
DocumentData *document = nullptr;
|
||||
not_null<EmojiPtr> emoji;
|
||||
QString replacement;
|
||||
};
|
||||
|
@ -56,7 +65,8 @@ private:
|
|||
void scrollByWheelEvent(not_null<QWheelEvent*> e);
|
||||
void paintFadings(Painter &p) const;
|
||||
|
||||
std::vector<Row> getRowsByQuery() const;
|
||||
[[nodiscard]] std::vector<Row> getRowsByQuery() const;
|
||||
[[nodiscard]] std::vector<Row> prependCustom(std::vector<Row> rows);
|
||||
void resizeToRows();
|
||||
void setSelected(
|
||||
int selected,
|
||||
|
@ -77,8 +87,21 @@ private:
|
|||
void scrollTo(int value, anim::type animated = anim::type::instant);
|
||||
void stopAnimations();
|
||||
|
||||
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance(
|
||||
not_null<DocumentData*> document);
|
||||
void scheduleRepaintTimer();
|
||||
void invokeRepaints();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
QString _query;
|
||||
std::vector<Row> _rows;
|
||||
base::flat_map<
|
||||
not_null<DocumentData*>,
|
||||
std::unique_ptr<CustomInstance>> _instances;
|
||||
base::flat_map<crl::time, crl::time> _repaints;
|
||||
bool _repaintTimerScheduled = false;
|
||||
base::Timer _repaintTimer;
|
||||
crl::time _repaintNext = 0;
|
||||
|
||||
std::optional<QPoint> _lastMousePosition;
|
||||
bool _mouseSelection = false;
|
||||
|
@ -96,7 +119,7 @@ private:
|
|||
int _dragScrollStart = -1;
|
||||
|
||||
rpl::event_stream<bool> _toggleAnimated;
|
||||
rpl::event_stream<QString> _triggered;
|
||||
rpl::event_stream<Chosen> _triggered;
|
||||
|
||||
};
|
||||
|
||||
|
@ -119,7 +142,8 @@ public:
|
|||
void setReplaceCallback(Fn<void(
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement)> callback);
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData)> callback);
|
||||
|
||||
static SuggestionsController *Init(
|
||||
not_null<QWidget*> outer,
|
||||
|
@ -135,7 +159,9 @@ private:
|
|||
void suggestionsUpdated(bool visible);
|
||||
void updateGeometry();
|
||||
void updateForceHidden();
|
||||
void replaceCurrent(const QString &replacement);
|
||||
void replaceCurrent(
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData);
|
||||
bool fieldFilter(not_null<QEvent*> event);
|
||||
bool outerFilter(not_null<QEvent*> event);
|
||||
|
||||
|
@ -149,7 +175,8 @@ private:
|
|||
Fn<void(
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement)> _replaceCallback;
|
||||
const QString &replacement,
|
||||
const QString &customEmojiData)> _replaceCallback;
|
||||
base::unique_qptr<InnerDropdown> _container;
|
||||
QPointer<SuggestionsWidget> _suggestions;
|
||||
base::unique_qptr<QObject> _fieldFilter;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 1d34c64da8bc234c4d5dd8ebaff7f249d897c7d7
|
||||
Subproject commit 0daf3d4ac70e587c80abe7685e7ad7512f6f39cf
|
Loading…
Add table
Reference in a new issue