mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-13 04:37:11 +02:00
Show tag names in Saved Messages.
This commit is contained in:
parent
55a174190e
commit
32462fca9b
7 changed files with 193 additions and 69 deletions
|
@ -422,10 +422,7 @@ void Reactions::scheduleMyTagsUpdate() {
|
|||
return;
|
||||
}
|
||||
_myTagsUpdateScheduled = false;
|
||||
_myTagsIds = _myTagsInfo | ranges::views::transform(
|
||||
&MyTagInfo::id
|
||||
) | ranges::to_vector;
|
||||
_myTags = resolveByIds(_myTagsIds, _unresolvedMyTags);
|
||||
_myTags = resolveByInfos(_myTagsInfo, _unresolvedMyTags);
|
||||
_myTagsUpdated.fire({});
|
||||
});
|
||||
}
|
||||
|
@ -854,10 +851,7 @@ void Reactions::updateGeneric(const MTPDmessages_stickerSet &data) {
|
|||
void Reactions::updateMyTags(const MTPDmessages_savedReactionTags &data) {
|
||||
_myTagsHash = data.vhash().v;
|
||||
_myTagsInfo = ListFromMTP(data);
|
||||
_myTagsIds = _myTagsInfo | ranges::views::transform(
|
||||
&MyTagInfo::id
|
||||
) | ranges::to_vector;
|
||||
_myTags = resolveByIds(_myTagsIds, _unresolvedMyTags);
|
||||
_myTags = resolveByInfos(_myTagsInfo, _unresolvedMyTags);
|
||||
_myTagsUpdated.fire({});
|
||||
}
|
||||
|
||||
|
@ -927,7 +921,7 @@ void Reactions::customEmojiResolveDone(not_null<DocumentData*> document) {
|
|||
}
|
||||
if (myTag) {
|
||||
_unresolvedMyTags.erase(k);
|
||||
_myTags = resolveByIds(_myTagsIds, _unresolvedMyTags);
|
||||
_myTags = resolveByInfos(_myTagsInfo, _unresolvedMyTags);
|
||||
}
|
||||
if (tag) {
|
||||
_unresolvedTags.erase(l);
|
||||
|
@ -980,6 +974,41 @@ std::vector<Reaction> Reactions::resolveByIds(
|
|||
return result;
|
||||
}
|
||||
|
||||
std::optional<Reaction> Reactions::resolveByInfo(const MyTagInfo &info) {
|
||||
const auto withInfo = [&](Reaction reaction) {
|
||||
reaction.title = info.title;
|
||||
reaction.count = info.count;
|
||||
return reaction;
|
||||
};
|
||||
if (const auto emoji = info.id.emoji(); !emoji.isEmpty()) {
|
||||
const auto i = ranges::find(_available, info.id, &Reaction::id);
|
||||
if (i != end(_available)) {
|
||||
return withInfo(*i);
|
||||
}
|
||||
} else if (const auto customId = info.id.custom()) {
|
||||
const auto document = _owner->document(customId);
|
||||
if (document->sticker()) {
|
||||
return withInfo(CustomReaction(document));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Reaction> Reactions::resolveByInfos(
|
||||
const std::vector<MyTagInfo> &infos,
|
||||
base::flat_set<ReactionId> &unresolved) {
|
||||
auto result = std::vector<Reaction>();
|
||||
result.reserve(infos.size());
|
||||
for (const auto &tag : infos) {
|
||||
if (const auto resolved = resolveByInfo(tag)) {
|
||||
result.push_back(*resolved);
|
||||
} else if (unresolved.emplace(tag.id).second) {
|
||||
resolve(tag.id);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Reactions::resolve(const ReactionId &id) {
|
||||
if (const auto emoji = id.emoji(); !emoji.isEmpty()) {
|
||||
refreshDefault();
|
||||
|
|
|
@ -34,6 +34,7 @@ struct Reaction {
|
|||
//not_null<DocumentData*> activateEffects;
|
||||
DocumentData *centerIcon = nullptr;
|
||||
DocumentData *aroundAnimation = nullptr;
|
||||
int count = 0;
|
||||
bool active = false;
|
||||
bool premium = false;
|
||||
};
|
||||
|
@ -167,6 +168,11 @@ private:
|
|||
[[nodiscard]] std::vector<Reaction> resolveByIds(
|
||||
const std::vector<ReactionId> &ids,
|
||||
base::flat_set<ReactionId> &unresolved);
|
||||
[[nodiscard]] std::optional<Reaction> resolveByInfo(
|
||||
const MyTagInfo &info);
|
||||
[[nodiscard]] std::vector<Reaction> resolveByInfos(
|
||||
const std::vector<MyTagInfo> &infos,
|
||||
base::flat_set<ReactionId> &unresolved);
|
||||
void resolve(const ReactionId &id);
|
||||
void applyFavorite(const ReactionId &id);
|
||||
void scheduleMyTagsUpdate();
|
||||
|
@ -193,7 +199,6 @@ private:
|
|||
std::vector<ReactionId> _recentIds;
|
||||
base::flat_set<ReactionId> _unresolvedRecent;
|
||||
std::vector<Reaction> _myTags;
|
||||
std::vector<ReactionId> _myTagsIds;
|
||||
std::vector<MyTagInfo> _myTagsInfo;
|
||||
base::flat_set<ReactionId> _unresolvedMyTags;
|
||||
std::vector<Reaction> _tags;
|
||||
|
|
|
@ -19,10 +19,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace Dialogs {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString ComposeText(const Data::Reaction &tag) {
|
||||
auto result = tag.title;
|
||||
if (!result.isEmpty() && tag.count > 0) {
|
||||
result.append(' ');
|
||||
}
|
||||
if (tag.count > 0) {
|
||||
result.append(QString::number(tag.count));
|
||||
}
|
||||
return TextUtilities::SingleLine(result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct SearchTags::Tag {
|
||||
Data::ReactionId id;
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> custom;
|
||||
QString text;
|
||||
int textWidth = 0;
|
||||
mutable QImage image;
|
||||
QRect geometry;
|
||||
ClickHandlerPtr link;
|
||||
|
@ -75,7 +91,7 @@ void SearchTags::fill(const std::vector<Data::Reaction> &list) {
|
|||
}
|
||||
}));
|
||||
};
|
||||
const auto push = [&](Data::ReactionId id) {
|
||||
const auto push = [&](Data::ReactionId id, const QString &text) {
|
||||
const auto customId = id.custom();
|
||||
_tags.push_back({
|
||||
.id = id,
|
||||
|
@ -84,6 +100,8 @@ void SearchTags::fill(const std::vector<Data::Reaction> &list) {
|
|||
customId,
|
||||
[=] { _repaintRequests.fire({}); })
|
||||
: nullptr),
|
||||
.text = text,
|
||||
.textWidth = st::reactionInlineTagFont->width(text),
|
||||
.link = link(id),
|
||||
.selected = ranges::contains(selected, id),
|
||||
});
|
||||
|
@ -92,11 +110,11 @@ void SearchTags::fill(const std::vector<Data::Reaction> &list) {
|
|||
}
|
||||
};
|
||||
for (const auto &reaction : list) {
|
||||
push(reaction.id);
|
||||
push(reaction.id, ComposeText(reaction));
|
||||
}
|
||||
for (const auto &reaction : _added) {
|
||||
if (!ranges::contains(_tags, reaction, &Tag::id)) {
|
||||
push(reaction);
|
||||
push(reaction, QString());
|
||||
}
|
||||
}
|
||||
if (_width > 0) {
|
||||
|
@ -107,26 +125,27 @@ void SearchTags::fill(const std::vector<Data::Reaction> &list) {
|
|||
void SearchTags::layout() {
|
||||
Expects(_width > 0);
|
||||
|
||||
if (_tags.empty()) {
|
||||
_height = 0;
|
||||
return;
|
||||
}
|
||||
const auto &bg = validateBg(false);
|
||||
const auto skip = st::dialogsSearchTagSkip;
|
||||
const auto size = bg.size() / bg.devicePixelRatio();
|
||||
const auto xsingle = size.width() + skip.x();
|
||||
const auto ysingle = size.height() + skip.y();
|
||||
const auto columns = std::max((_width + skip.x()) / xsingle, 1);
|
||||
const auto rows = (_tags.size() + columns - 1) / columns;
|
||||
for (auto row = 0; row != rows; ++row) {
|
||||
for (auto column = 0; column != columns; ++column) {
|
||||
const auto index = row * columns + column;
|
||||
if (index >= _tags.size()) {
|
||||
break;
|
||||
}
|
||||
const auto x = column * xsingle;
|
||||
const auto y = row * ysingle;
|
||||
_tags[index].geometry = QRect(QPoint(x, y), size);
|
||||
const auto xbase = size.width();
|
||||
const auto ybase = size.height();
|
||||
auto x = 0;
|
||||
auto y = 0;
|
||||
for (auto &tag : _tags) {
|
||||
const auto width = xbase + tag.textWidth;
|
||||
if (x > 0 && x + width > _width) {
|
||||
x = 0;
|
||||
y += ybase + skip.y();
|
||||
}
|
||||
tag.geometry = QRect(x, y, width, xbase);
|
||||
x += width + skip.x();
|
||||
}
|
||||
const auto bottom = st::dialogsSearchTagBottom;
|
||||
_height = rows ? (rows * ysingle - skip.y() + bottom) : 0;
|
||||
_height = y + ybase + st::dialogsSearchTagBottom;
|
||||
}
|
||||
|
||||
void SearchTags::resizeToWidth(int width) {
|
||||
|
@ -212,7 +231,8 @@ void SearchTags::paint(
|
|||
const auto padding = st::reactionInlinePadding;
|
||||
for (const auto &tag : _tags) {
|
||||
const auto geometry = tag.geometry.translated(position);
|
||||
p.drawImage(geometry.topLeft(), validateBg(tag.selected));
|
||||
paintBackground(p, geometry, tag.selected);
|
||||
paintText(p, geometry, tag);
|
||||
if (!tag.custom && tag.image.isNull()) {
|
||||
tag.image = _owner->reactions().resolveImageFor(
|
||||
tag.id,
|
||||
|
@ -239,16 +259,59 @@ void SearchTags::paint(
|
|||
}
|
||||
}
|
||||
|
||||
void SearchTags::paintBackground(
|
||||
QPainter &p,
|
||||
QRect geometry,
|
||||
bool selected) const {
|
||||
const auto &image = validateBg(selected);
|
||||
const auto ratio = int(image.devicePixelRatio());
|
||||
const auto size = image.size() / ratio;
|
||||
if (const auto fill = geometry.width() - size.width(); fill > 0) {
|
||||
const auto left = size.width() / 2;
|
||||
const auto right = size.width() - left;
|
||||
const auto x = geometry.x();
|
||||
const auto y = geometry.y();
|
||||
p.drawImage(
|
||||
QRect(x, y, left, size.height()),
|
||||
image,
|
||||
QRect(QPoint(), QSize(left, size.height()) * ratio));
|
||||
p.fillRect(
|
||||
QRect(x + left, y, fill, size.height()),
|
||||
bgColor(selected));
|
||||
p.drawImage(
|
||||
QRect(x + left + fill, y, right, size.height()),
|
||||
image,
|
||||
QRect(left * ratio, 0, right * ratio, size.height() * ratio));
|
||||
} else {
|
||||
p.drawImage(geometry.topLeft(), image);
|
||||
}
|
||||
}
|
||||
|
||||
void SearchTags::paintText(QPainter &p, QRect geometry, const Tag &tag) const {
|
||||
using namespace HistoryView::Reactions;
|
||||
|
||||
if (tag.text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
p.setPen(tag.selected ? st::dialogsTextFgActive : st::windowSubTextFg);
|
||||
p.setFont(st::reactionInlineTagFont);
|
||||
const auto x = geometry.x() + st::reactionInlineTagNamePosition.x();
|
||||
const auto y = geometry.y() + st::reactionInlineTagNamePosition.y();
|
||||
p.drawText(x, y + st::reactionInlineTagFont->ascent, tag.text);
|
||||
}
|
||||
|
||||
QColor SearchTags::bgColor(bool selected) const {
|
||||
return selected
|
||||
? st::dialogsBgActive->c
|
||||
: st::dialogsBgOver->c;
|
||||
}
|
||||
|
||||
const QImage &SearchTags::validateBg(bool selected) const {
|
||||
using namespace HistoryView::Reactions;
|
||||
auto &image = selected ? _selectedBg : _normalBg;
|
||||
if (image.isNull()) {
|
||||
const auto tagBg = selected
|
||||
? st::dialogsBgActive->c
|
||||
: st::dialogsBgOver->c;
|
||||
const auto dotBg = selected
|
||||
? anim::with_alpha(tagBg, InlineList::TagDotAlpha())
|
||||
: st::windowSubTextFg->c;
|
||||
const auto tagBg = bgColor(selected);
|
||||
const auto dotBg = st::transparent->c;
|
||||
image = InlineList::PrepareTagBg(tagBg, dotBg);
|
||||
}
|
||||
return image;
|
||||
|
|
|
@ -59,7 +59,13 @@ private:
|
|||
const QColor &textColor) const;
|
||||
void layout();
|
||||
[[nodiscard]] std::vector<Data::ReactionId> collectSelected() const;
|
||||
[[nodiscard]] QColor bgColor(bool selected) const;
|
||||
[[nodiscard]] const QImage &validateBg(bool selected) const;
|
||||
void paintBackground(
|
||||
QPainter &p,
|
||||
QRect geometry,
|
||||
bool selected) const;
|
||||
void paintText(QPainter &p, QRect geometry, const Tag &tag) const;
|
||||
|
||||
const not_null<Data::Session*> _owner;
|
||||
std::vector<Data::ReactionId> _added;
|
||||
|
|
|
@ -51,9 +51,9 @@ struct InlineList::Button {
|
|||
mutable std::unique_ptr<Ui::Text::CustomEmoji> custom;
|
||||
std::unique_ptr<Userpics> userpics;
|
||||
ReactionId id;
|
||||
QString countText;
|
||||
QString text;
|
||||
int textWidth = 0;
|
||||
int count = 0;
|
||||
int countTextWidth = 0;
|
||||
bool chosen = false;
|
||||
bool tag = false;
|
||||
};
|
||||
|
@ -120,20 +120,23 @@ void InlineList::layoutButtons() {
|
|||
return not_null{ &reaction };
|
||||
}) | ranges::to_vector;
|
||||
const auto tags = _data.flags & Data::Flag::Tags;
|
||||
const auto &list = _owner->list(::Data::Reactions::Type::All);
|
||||
ranges::sort(sorted, [&](
|
||||
not_null<const MessageReaction*> a,
|
||||
not_null<const MessageReaction*> b) {
|
||||
const auto acount = a->count - (a->my ? 1 : 0);
|
||||
const auto bcount = b->count - (b->my ? 1 : 0);
|
||||
if (acount > bcount) {
|
||||
return true;
|
||||
} else if (acount < bcount) {
|
||||
return false;
|
||||
}
|
||||
return ranges::find(list, a->id, &::Data::Reaction::id)
|
||||
< ranges::find(list, b->id, &::Data::Reaction::id);
|
||||
});
|
||||
const auto &infos = _owner->myTagsInfo();
|
||||
if (!tags) {
|
||||
const auto &list = _owner->list(::Data::Reactions::Type::All);
|
||||
ranges::sort(sorted, [&](
|
||||
not_null<const MessageReaction*> a,
|
||||
not_null<const MessageReaction*> b) {
|
||||
const auto acount = a->count - (a->my ? 1 : 0);
|
||||
const auto bcount = b->count - (b->my ? 1 : 0);
|
||||
if (acount > bcount) {
|
||||
return true;
|
||||
} else if (acount < bcount) {
|
||||
return false;
|
||||
}
|
||||
return ranges::find(list, a->id, &::Data::Reaction::id)
|
||||
< ranges::find(list, b->id, &::Data::Reaction::id);
|
||||
});
|
||||
}
|
||||
|
||||
_hasCustomEmoji = false;
|
||||
auto buttons = std::vector<Button>();
|
||||
|
@ -145,7 +148,10 @@ void InlineList::layoutButtons() {
|
|||
? std::move(*i)
|
||||
: prepareButtonWithId(id));
|
||||
if (tags) {
|
||||
setButtonTag(buttons.back());
|
||||
const auto i = ranges::find(infos, id, &::Data::MyTagInfo::id);
|
||||
setButtonTag(
|
||||
buttons.back(),
|
||||
(i == end(infos)) ? QString() : i->title);
|
||||
} else if (const auto j = _data.recent.find(id)
|
||||
; j != end(_data.recent) && !j->second.empty()) {
|
||||
setButtonUserpics(buttons.back(), j->second);
|
||||
|
@ -172,13 +178,15 @@ InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void InlineList::setButtonTag(Button &button) {
|
||||
if (button.tag) {
|
||||
void InlineList::setButtonTag(Button &button, const QString &title) {
|
||||
if (button.tag && button.text == title) {
|
||||
return;
|
||||
}
|
||||
button.userpics = nullptr;
|
||||
button.count = 0;
|
||||
button.tag = true;
|
||||
button.text = title;
|
||||
button.textWidth = st::reactionInlineTagFont->width(button.text);
|
||||
}
|
||||
|
||||
void InlineList::setButtonCount(Button &button, int count) {
|
||||
|
@ -188,8 +196,8 @@ void InlineList::setButtonCount(Button &button, int count) {
|
|||
button.userpics = nullptr;
|
||||
button.count = count;
|
||||
button.tag = false;
|
||||
button.countText = Lang::FormatCountToShort(count).string;
|
||||
button.countTextWidth = st::semiboldFont->width(button.countText);
|
||||
button.text = Lang::FormatCountToShort(count).string;
|
||||
button.textWidth = st::semiboldFont->width(button.text);
|
||||
}
|
||||
|
||||
void InlineList::setButtonUserpics(
|
||||
|
@ -265,10 +273,12 @@ QSize InlineList::countOptimalSize() {
|
|||
const auto height = padding.top() + size + padding.bottom();
|
||||
for (auto &button : _buttons) {
|
||||
const auto width = button.tag
|
||||
? widthBaseTag
|
||||
? (widthBaseTag
|
||||
+ button.textWidth
|
||||
+ (button.textWidth ? st::reactionInlineSkip : 0))
|
||||
: button.userpics
|
||||
? (widthBaseUserpics + userpicsWidth(button))
|
||||
: (widthBaseCount + button.countTextWidth);
|
||||
: (widthBaseCount + button.textWidth);
|
||||
button.geometry.setSize({ width, height });
|
||||
x += width + between;
|
||||
}
|
||||
|
@ -360,7 +370,7 @@ void InlineList::paint(
|
|||
const auto tags = (_data.flags & Data::Flag::Tags);
|
||||
const auto inbubble = (_data.flags & Data::Flag::InBubble);
|
||||
const auto flipped = (_data.flags & Data::Flag::Flipped);
|
||||
p.setFont(st::semiboldFont);
|
||||
p.setFont(tags ? st::reactionInlineTagFont : st::semiboldFont);
|
||||
for (const auto &button : _buttons) {
|
||||
if (context.reactionInfo
|
||||
&& button.animation
|
||||
|
@ -460,7 +470,7 @@ void InlineList::paint(
|
|||
.target = image,
|
||||
});
|
||||
}
|
||||
if (tags || bubbleProgress == 0.) {
|
||||
if ((tags && !button.textWidth) || bubbleProgress == 0.) {
|
||||
p.setOpacity(1.);
|
||||
continue;
|
||||
}
|
||||
|
@ -473,12 +483,19 @@ void InlineList::paint(
|
|||
button.userpics->image);
|
||||
} else {
|
||||
p.setPen(textFg);
|
||||
const auto textLeft = tags
|
||||
? (left
|
||||
- padding.left()
|
||||
+ st::reactionInlineTagNamePosition.x())
|
||||
: (left + size + st::reactionInlineSkip);
|
||||
const auto textTop = geometry.y()
|
||||
+ ((geometry.height() - st::semiboldFont->height) / 2);
|
||||
p.drawText(
|
||||
left + size + st::reactionInlineSkip,
|
||||
textTop + st::semiboldFont->ascent,
|
||||
button.countText);
|
||||
+ (tags
|
||||
? st::reactionInlineTagNamePosition.y()
|
||||
: ((geometry.height() - st::semiboldFont->height) / 2));
|
||||
const auto font = tags
|
||||
? st::reactionInlineTagFont
|
||||
: st::semiboldFont;
|
||||
p.drawText(textLeft, textTop + font->ascent, button.text);
|
||||
}
|
||||
if (!bubbleReady) {
|
||||
p.setOpacity(1.);
|
||||
|
@ -569,9 +586,11 @@ QImage InlineList::PrepareTagBg(QColor tagBg, QColor dotBg) {
|
|||
p.setBrush(tagBg);
|
||||
p.drawPath(path);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(dotBg);
|
||||
p.drawEllipse(dot);
|
||||
if (dotBg.alpha() > 0) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(dotBg);
|
||||
p.drawEllipse(dot);
|
||||
}
|
||||
|
||||
p.end();
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ private:
|
|||
void layout();
|
||||
void layoutButtons();
|
||||
|
||||
void setButtonTag(Button &button);
|
||||
void setButtonTag(Button &button, const QString &title);
|
||||
void setButtonCount(Button &button, int count);
|
||||
void setButtonUserpics(
|
||||
Button &button,
|
||||
|
|
|
@ -864,6 +864,8 @@ reactionInlineTagRightRadius: 3px;
|
|||
reactionInlineTagArrow: 5px;
|
||||
reactionInlineTagDot: 5px;
|
||||
reactionInlineTagDotSkip: 2px;
|
||||
reactionInlineTagFont: font(12px);
|
||||
reactionInlineTagNamePosition: point(26px, 2px);
|
||||
reactionInlineBetween: 4px;
|
||||
reactionInlineInBubbleLeft: -3px;
|
||||
reactionInlineUserpicsPadding: margins(1px, 1px, 1px, 1px);
|
||||
|
|
Loading…
Add table
Reference in a new issue