Show full reply names with icons.
BIN
Telegram/Resources/icons/chat/reply_type_channel.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
Telegram/Resources/icons/chat/reply_type_channel@2x.png
Normal file
After Width: | Height: | Size: 474 B |
BIN
Telegram/Resources/icons/chat/reply_type_channel@3x.png
Normal file
After Width: | Height: | Size: 619 B |
BIN
Telegram/Resources/icons/chat/reply_type_group.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
Telegram/Resources/icons/chat/reply_type_group@2x.png
Normal file
After Width: | Height: | Size: 610 B |
BIN
Telegram/Resources/icons/chat/reply_type_group@3x.png
Normal file
After Width: | Height: | Size: 890 B |
BIN
Telegram/Resources/icons/chat/reply_type_user.png
Normal file
After Width: | Height: | Size: 239 B |
BIN
Telegram/Resources/icons/chat/reply_type_user@2x.png
Normal file
After Width: | Height: | Size: 416 B |
BIN
Telegram/Resources/icons/chat/reply_type_user@3x.png
Normal file
After Width: | Height: | Size: 554 B |
|
@ -89,6 +89,10 @@ private:
|
||||||
: FrameSizeFromTag(tag);
|
: FrameSizeFromTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QString InternalPrefix() {
|
||||||
|
return u"internal:"_q;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class CustomEmojiLoader final
|
class CustomEmojiLoader final
|
||||||
|
@ -514,6 +518,9 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||||
Fn<void()> update,
|
Fn<void()> update,
|
||||||
SizeTag tag,
|
SizeTag tag,
|
||||||
int sizeOverride) {
|
int sizeOverride) {
|
||||||
|
if (data.startsWith(InternalPrefix())) {
|
||||||
|
return internal(data);
|
||||||
|
}
|
||||||
const auto parsed = ParseCustomEmojiData(data);
|
const auto parsed = ParseCustomEmojiData(data);
|
||||||
return parsed
|
return parsed
|
||||||
? create(parsed, std::move(update), tag, sizeOverride)
|
? create(parsed, std::move(update), tag, sizeOverride)
|
||||||
|
@ -540,6 +547,18 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::internal(
|
||||||
|
QStringView data) {
|
||||||
|
const auto index = data.mid(InternalPrefix().size()).toInt();
|
||||||
|
Assert(index >= 0 && index < _internalEmoji.size());
|
||||||
|
|
||||||
|
auto &info = _internalEmoji[index];
|
||||||
|
return std::make_unique<Ui::CustomEmoji::Internal>(
|
||||||
|
data.toString(),
|
||||||
|
info.image,
|
||||||
|
info.textColor);
|
||||||
|
}
|
||||||
|
|
||||||
void CustomEmojiManager::resolve(
|
void CustomEmojiManager::resolve(
|
||||||
QStringView data,
|
QStringView data,
|
||||||
not_null<Listener*> listener) {
|
not_null<Listener*> listener) {
|
||||||
|
@ -885,6 +904,34 @@ uint64 CustomEmojiManager::coloredSetId() const {
|
||||||
return _coloredSetId;
|
return _coloredSetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CustomEmojiManager::registerInternalEmoji(
|
||||||
|
QImage emoji,
|
||||||
|
bool textColor) {
|
||||||
|
_internalEmoji.push_back({ std::move(emoji), textColor });
|
||||||
|
return InternalPrefix() + QString::number(_internalEmoji.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CustomEmojiManager::registerInternalEmoji(
|
||||||
|
const style::icon &icon,
|
||||||
|
bool textColor) {
|
||||||
|
const auto i = _iconEmoji.find(&icon);
|
||||||
|
if (i != end(_iconEmoji)) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
auto image = QImage(
|
||||||
|
icon.size() * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
image.fill(Qt::transparent);
|
||||||
|
image.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
auto p = QPainter(&image);
|
||||||
|
icon.paint(p, 0, 0, icon.width());
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
const auto result = registerInternalEmoji(std::move(image), textColor);
|
||||||
|
_iconEmoji.emplace(&icon, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int FrameSizeFromTag(SizeTag tag) {
|
int FrameSizeFromTag(SizeTag tag) {
|
||||||
const auto emoji = EmojiSizeFromTag(tag);
|
const auto emoji = EmojiSizeFromTag(tag);
|
||||||
const auto factor = style::DevicePixelRatio();
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
|
|
@ -83,11 +83,22 @@ public:
|
||||||
[[nodiscard]] Main::Session &session() const;
|
[[nodiscard]] Main::Session &session() const;
|
||||||
[[nodiscard]] Session &owner() const;
|
[[nodiscard]] Session &owner() const;
|
||||||
|
|
||||||
|
[[nodiscard]] QString registerInternalEmoji(
|
||||||
|
QImage emoji,
|
||||||
|
bool textColor = true);
|
||||||
|
[[nodiscard]] QString registerInternalEmoji(
|
||||||
|
const style::icon &icon,
|
||||||
|
bool textColor = true);
|
||||||
|
|
||||||
[[nodiscard]] uint64 coloredSetId() const;
|
[[nodiscard]] uint64 coloredSetId() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr auto kSizeCount = int(SizeTag::kCount);
|
static constexpr auto kSizeCount = int(SizeTag::kCount);
|
||||||
|
|
||||||
|
struct InternalEmojiData {
|
||||||
|
QImage image;
|
||||||
|
bool textColor = true;
|
||||||
|
};
|
||||||
struct RepaintBunch {
|
struct RepaintBunch {
|
||||||
crl::time when = 0;
|
crl::time when = 0;
|
||||||
std::vector<base::weak_ptr<Ui::CustomEmoji::Instance>> instances;
|
std::vector<base::weak_ptr<Ui::CustomEmoji::Instance>> instances;
|
||||||
|
@ -131,6 +142,8 @@ private:
|
||||||
SizeTag tag,
|
SizeTag tag,
|
||||||
int sizeOverride,
|
int sizeOverride,
|
||||||
LoaderFactory factory);
|
LoaderFactory factory);
|
||||||
|
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> internal(
|
||||||
|
QStringView data);
|
||||||
[[nodiscard]] static int SizeIndex(SizeTag tag);
|
[[nodiscard]] static int SizeIndex(SizeTag tag);
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
|
@ -163,6 +176,9 @@ private:
|
||||||
bool _repaintTimerScheduled = false;
|
bool _repaintTimerScheduled = false;
|
||||||
bool _requestSetsScheduled = false;
|
bool _requestSetsScheduled = false;
|
||||||
|
|
||||||
|
std::vector<InternalEmojiData> _internalEmoji;
|
||||||
|
base::flat_map<not_null<const style::icon*>, QString> _iconEmoji;
|
||||||
|
|
||||||
#if 0 // inject-to-on_main
|
#if 0 // inject-to-on_main
|
||||||
crl::time _repaintsLastAdded = 0;
|
crl::time _repaintsLastAdded = 0;
|
||||||
rpl::lifetime _repaintsLifetime;
|
rpl::lifetime _repaintsLifetime;
|
||||||
|
|
|
@ -477,7 +477,6 @@ HistoryMessageReply &HistoryMessageReply::operator=(
|
||||||
HistoryMessageReply::~HistoryMessageReply() {
|
HistoryMessageReply::~HistoryMessageReply() {
|
||||||
// clearData() should be called by holder.
|
// clearData() should be called by holder.
|
||||||
Expects(resolvedMessage.empty());
|
Expects(resolvedMessage.empty());
|
||||||
Expects(originalVia == nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryMessageReply::updateData(
|
bool HistoryMessageReply::updateData(
|
||||||
|
@ -523,44 +522,42 @@ bool HistoryMessageReply::updateData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto repaint = [=] { holder->customEmojiRepaint(); };
|
||||||
|
const auto context = Core::MarkedTextContext{
|
||||||
|
.session = &holder->history()->session(),
|
||||||
|
.customEmojiRepaint = repaint,
|
||||||
|
};
|
||||||
const auto external = this->external();
|
const auto external = this->external();
|
||||||
if (resolvedMessage
|
_multiline = 0;
|
||||||
|
// #TODO !_fields.storyId && (external || !_fields.quote.empty());
|
||||||
|
|
||||||
|
const auto displaying = resolvedMessage
|
||||||
|| resolvedStory
|
|| resolvedStory
|
||||||
|| (external && (!_fields.messageId || force))) {
|
|| (external && (!_fields.messageId || force));
|
||||||
const auto repaint = [=] { holder->customEmojiRepaint(); };
|
_displaying = displaying ? 1 : 0;
|
||||||
const auto context = Core::MarkedTextContext{
|
|
||||||
.session = &holder->history()->session(),
|
|
||||||
.customEmojiRepaint = repaint,
|
|
||||||
};
|
|
||||||
const auto text = !_fields.quote.empty()
|
|
||||||
? _fields.quote
|
|
||||||
: resolvedMessage
|
|
||||||
? resolvedMessage->inReplyText()
|
|
||||||
: resolvedStory
|
|
||||||
? resolvedStory->inReplyText()
|
|
||||||
: TextWithEntities{ u"..."_q };
|
|
||||||
_text.setMarkedText(
|
|
||||||
st::defaultTextStyle,
|
|
||||||
text,
|
|
||||||
Ui::DialogTextOptions(),
|
|
||||||
context);
|
|
||||||
|
|
||||||
updateName(holder);
|
const auto unavailable = !resolvedMessage
|
||||||
|
&& !resolvedStory
|
||||||
|
&& ((!_fields.storyId && !_fields.messageId) || force);
|
||||||
|
_unavailable = unavailable ? 1 : 0;
|
||||||
|
|
||||||
|
const auto text = !_fields.quote.empty()
|
||||||
|
? _fields.quote
|
||||||
|
: resolvedMessage
|
||||||
|
? resolvedMessage->inReplyText()
|
||||||
|
: resolvedStory
|
||||||
|
? resolvedStory->inReplyText()
|
||||||
|
: TextWithEntities{ u"..."_q };
|
||||||
|
_text.setMarkedText(
|
||||||
|
st::defaultTextStyle,
|
||||||
|
text,
|
||||||
|
Ui::DialogTextOptions(),
|
||||||
|
context);
|
||||||
|
|
||||||
|
updateName(holder);
|
||||||
|
|
||||||
|
if (_displaying) {
|
||||||
setLinkFrom(holder);
|
setLinkFrom(holder);
|
||||||
if (resolvedMessage
|
|
||||||
&& !resolvedMessage->Has<HistoryMessageForwarded>()) {
|
|
||||||
if (const auto bot = resolvedMessage->viaBot()) {
|
|
||||||
originalVia = std::make_unique<HistoryMessageVia>();
|
|
||||||
originalVia->create(
|
|
||||||
&holder->history()->owner(),
|
|
||||||
peerToUser(bot->id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resolvedMessage && !resolvedStory) {
|
|
||||||
_unavailable = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto media = resolvedMessage
|
const auto media = resolvedMessage
|
||||||
? resolvedMessage->media()
|
? resolvedMessage->media()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
@ -642,7 +639,6 @@ void HistoryMessageReply::setTopMessageId(MsgId topMessageId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
|
void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
|
||||||
originalVia = nullptr;
|
|
||||||
if (resolvedMessage) {
|
if (resolvedMessage) {
|
||||||
holder->history()->owner().unregisterDependentMessage(
|
holder->history()->owner().unregisterDependentMessage(
|
||||||
holder,
|
holder,
|
||||||
|
@ -658,6 +654,12 @@ void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
|
||||||
_name.clear();
|
_name.clear();
|
||||||
_text.clear();
|
_text.clear();
|
||||||
_unavailable = 1;
|
_unavailable = 1;
|
||||||
|
_displaying = 0;
|
||||||
|
_expandable = 0;
|
||||||
|
if (_multiline) {
|
||||||
|
holder->history()->owner().requestItemResize(holder);
|
||||||
|
_multiline = 0;
|
||||||
|
}
|
||||||
refreshReplyToMedia();
|
refreshReplyToMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,9 +693,10 @@ PeerData *HistoryMessageReply::sender(not_null<HistoryItem*> holder) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HistoryMessageReply::senderName(
|
QString HistoryMessageReply::senderName(
|
||||||
not_null<HistoryItem*> holder) const {
|
not_null<HistoryItem*> holder,
|
||||||
|
bool shorten) const {
|
||||||
if (const auto peer = sender(holder)) {
|
if (const auto peer = sender(holder)) {
|
||||||
return senderName(peer);
|
return senderName(peer, shorten);
|
||||||
} else if (!resolvedMessage) {
|
} else if (!resolvedMessage) {
|
||||||
return _fields.externalSenderName;
|
return _fields.externalSenderName;
|
||||||
} else if (holder->Has<HistoryMessageForwarded>()) {
|
} else if (holder->Has<HistoryMessageForwarded>()) {
|
||||||
|
@ -708,11 +711,11 @@ QString HistoryMessageReply::senderName(
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HistoryMessageReply::senderName(not_null<PeerData*> peer) const {
|
QString HistoryMessageReply::senderName(
|
||||||
if (const auto user = originalVia ? peer->asUser() : nullptr) {
|
not_null<PeerData*> peer,
|
||||||
return user->firstName;
|
bool shorten) const {
|
||||||
}
|
const auto user = shorten ? peer->asUser() : nullptr;
|
||||||
return peer->name();
|
return user ? user->firstName : peer->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryMessageReply::isNameUpdated(
|
bool HistoryMessageReply::isNameUpdated(
|
||||||
|
@ -729,47 +732,101 @@ bool HistoryMessageReply::isNameUpdated(
|
||||||
void HistoryMessageReply::updateName(
|
void HistoryMessageReply::updateName(
|
||||||
not_null<HistoryItem*> holder,
|
not_null<HistoryItem*> holder,
|
||||||
std::optional<PeerData*> resolvedSender) const {
|
std::optional<PeerData*> resolvedSender) const {
|
||||||
const auto peer = resolvedSender.value_or(sender(holder));
|
auto viaBotUsername = QString();
|
||||||
const auto name = peer ? senderName(peer) : senderName(holder);
|
if (resolvedMessage
|
||||||
|
&& !resolvedMessage->Has<HistoryMessageForwarded>()) {
|
||||||
|
if (const auto bot = resolvedMessage->viaBot()) {
|
||||||
|
viaBotUsername = bot->username();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto sender = resolvedSender.value_or(this->sender(holder));
|
||||||
|
const auto externalPeer = _fields.externalPeerId
|
||||||
|
? holder->history()->owner().peer(_fields.externalPeerId).get()
|
||||||
|
: nullptr;
|
||||||
|
const auto groupNameAdded = (externalPeer && externalPeer != sender);
|
||||||
|
const auto shorten = !viaBotUsername.isEmpty() || groupNameAdded;
|
||||||
|
const auto name = sender
|
||||||
|
? senderName(sender, shorten)
|
||||||
|
: senderName(holder, shorten);
|
||||||
const auto hasPreview = (resolvedStory
|
const auto hasPreview = (resolvedStory
|
||||||
&& resolvedStory->hasReplyPreview())
|
&& resolvedStory->hasReplyPreview())
|
||||||
|| (resolvedMessage
|
|| (resolvedMessage
|
||||||
&& resolvedMessage->media()
|
&& resolvedMessage->media()
|
||||||
&& resolvedMessage->media()->hasReplyPreview());
|
&& resolvedMessage->media()->hasReplyPreview());
|
||||||
const auto textLeft = hasPreview
|
const auto previewSkip = st::messageQuoteStyle.outline
|
||||||
? (st::messageQuoteStyle.outline
|
+ st::historyReplyPreviewMargin.left()
|
||||||
+ st::historyReplyPreviewMargin.left()
|
+ st::historyReplyPreview
|
||||||
+ st::historyReplyPreview
|
+ st::historyReplyPreviewMargin.right()
|
||||||
+ st::historyReplyPreviewMargin.right())
|
- st::historyReplyPadding.left();
|
||||||
: st::historyReplyPadding.left();
|
const auto peerIcon = [](PeerData *peer) {
|
||||||
if (!name.isEmpty()) {
|
return !peer
|
||||||
_name.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
|
? &st::historyReplyUser
|
||||||
if (peer) {
|
: peer->isBroadcast()
|
||||||
_nameVersion = peer->nameVersion();
|
? &st::historyReplyChannel
|
||||||
}
|
: (peer->isChannel() || peer->isChat())
|
||||||
const auto w = _name.maxWidth()
|
? &st::historyReplyGroup
|
||||||
+ (originalVia
|
: &st::historyReplyUser;
|
||||||
? (st::msgServiceFont->spacew + originalVia->maxWidth)
|
};
|
||||||
: 0)
|
const auto peerEmoji = [&](PeerData *peer) {
|
||||||
+ (_fields.quote.empty()
|
const auto owner = &holder->history()->owner();
|
||||||
? 0
|
return Ui::Text::SingleCustomEmoji(
|
||||||
: st::messageTextStyle.blockquote.icon.width());
|
owner->customEmojiManager().registerInternalEmoji(
|
||||||
_maxWidth = std::max(
|
*peerIcon(peer)));
|
||||||
w,
|
};
|
||||||
std::min(_text.maxWidth(), st::maxSignatureSize))
|
auto nameFull = TextWithEntities();
|
||||||
+ (_fields.storyId
|
if (!groupNameAdded && !_fields.storyId) {
|
||||||
? (st::dialogsMiniReplyStory.skipText
|
nameFull.append(peerEmoji(sender));
|
||||||
+ st::dialogsMiniReplyStory.icon.icon.width())
|
|
||||||
: 0);
|
|
||||||
} else {
|
|
||||||
_maxWidth = st::msgDateFont->width(statePhrase());
|
|
||||||
}
|
}
|
||||||
_maxWidth = textLeft
|
nameFull.append(name);
|
||||||
|
if (groupNameAdded) {
|
||||||
|
nameFull.append(peerEmoji(externalPeer));
|
||||||
|
nameFull.append(externalPeer->name());
|
||||||
|
}
|
||||||
|
if (!viaBotUsername.isEmpty()) {
|
||||||
|
nameFull.append(u" @"_q).append(viaBotUsername);
|
||||||
|
}
|
||||||
|
const auto context = Core::MarkedTextContext{
|
||||||
|
.session = &holder->history()->session(),
|
||||||
|
.customEmojiRepaint = [] {},
|
||||||
|
.customEmojiLoopLimit = 1,
|
||||||
|
};
|
||||||
|
_name.setMarkedText(
|
||||||
|
st::fwdTextStyle,
|
||||||
|
nameFull,
|
||||||
|
Ui::NameTextOptions(),
|
||||||
|
context);
|
||||||
|
if (sender) {
|
||||||
|
_nameVersion = sender->nameVersion();
|
||||||
|
}
|
||||||
|
const auto nameMaxWidth = previewSkip
|
||||||
|
+ _name.maxWidth()
|
||||||
|
+ (hasQuoteIcon()
|
||||||
|
? st::messageTextStyle.blockquote.icon.width()
|
||||||
|
: 0);
|
||||||
|
const auto storySkip = _fields.storyId
|
||||||
|
? (st::dialogsMiniReplyStory.skipText
|
||||||
|
+ st::dialogsMiniReplyStory.icon.icon.width())
|
||||||
|
: 0;
|
||||||
|
const auto optimalTextSize = _multiline
|
||||||
|
? countMultilineOptimalSize(previewSkip)
|
||||||
|
: QSize(
|
||||||
|
(previewSkip
|
||||||
|
+ storySkip
|
||||||
|
+ std::min(_text.maxWidth(), st::maxSignatureSize)),
|
||||||
|
st::normalFont->height);
|
||||||
|
_maxWidth = std::max(nameMaxWidth, optimalTextSize.width());
|
||||||
|
if (!_displaying) {
|
||||||
|
const auto phraseWidth = st::msgDateFont->width(statePhrase());
|
||||||
|
_maxWidth = _unavailable
|
||||||
|
? phraseWidth
|
||||||
|
: std::max(_maxWidth, phraseWidth);
|
||||||
|
}
|
||||||
|
_maxWidth = st::historyReplyPadding.left()
|
||||||
+ _maxWidth
|
+ _maxWidth
|
||||||
+ st::historyReplyPadding.right();
|
+ st::historyReplyPadding.right();
|
||||||
_minHeight = st::historyReplyPadding.top()
|
_minHeight = st::historyReplyPadding.top()
|
||||||
+ st::msgServiceNameFont->height
|
+ st::msgServiceNameFont->height
|
||||||
+ st::normalFont->height
|
+ optimalTextSize.height()
|
||||||
+ st::historyReplyPadding.bottom();
|
+ st::historyReplyPadding.bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,17 +842,11 @@ int HistoryMessageReply::resizeToWidth(int width) const {
|
||||||
+ st::historyReplyPreview
|
+ st::historyReplyPreview
|
||||||
+ st::historyReplyPreviewMargin.right())
|
+ st::historyReplyPreviewMargin.right())
|
||||||
: st::historyReplyPadding.left();
|
: st::historyReplyPadding.left();
|
||||||
if (originalVia) {
|
if (width >= _maxWidth || !_multiline) {
|
||||||
originalVia->resize(width
|
|
||||||
- textLeft
|
|
||||||
- st::historyReplyPadding.right()
|
|
||||||
- _name.maxWidth()
|
|
||||||
- st::msgServiceFont->spacew);
|
|
||||||
}
|
|
||||||
if (width >= _maxWidth) {
|
|
||||||
_height = _minHeight;
|
_height = _minHeight;
|
||||||
return height();
|
return height();
|
||||||
}
|
}
|
||||||
|
// #TODO
|
||||||
_height = _minHeight;
|
_height = _minHeight;
|
||||||
return height();
|
return height();
|
||||||
}
|
}
|
||||||
|
@ -826,6 +877,15 @@ void HistoryMessageReply::storyRemoved(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HistoryMessageReply::hasQuoteIcon() const {
|
||||||
|
return _fields.manualQuote && !_fields.quote.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize HistoryMessageReply::countMultilineOptimalSize(
|
||||||
|
int firstLineSkip) const {
|
||||||
|
return QSize(); // #TODO
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryMessageReply::paint(
|
void HistoryMessageReply::paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
not_null<const HistoryView::Element*> holder,
|
not_null<const HistoryView::Element*> holder,
|
||||||
|
@ -839,7 +899,7 @@ void HistoryMessageReply::paint(
|
||||||
|
|
||||||
y += st::historyReplyTop;
|
y += st::historyReplyTop;
|
||||||
const auto rect = QRect(x, y, w, _height);
|
const auto rect = QRect(x, y, w, _height);
|
||||||
const auto hasQuote = _fields.manualQuote && !_fields.quote.empty();
|
const auto hasQuote = hasQuoteIcon();
|
||||||
const auto selected = context.selected();
|
const auto selected = context.selected();
|
||||||
const auto colorPeer = resolvedMessage
|
const auto colorPeer = resolvedMessage
|
||||||
? resolvedMessage->displayFrom()
|
? resolvedMessage->displayFrom()
|
||||||
|
@ -969,10 +1029,6 @@ void HistoryMessageReply::paint(
|
||||||
? FromNameFg(context, colorIndexPlusOne - 1)
|
? FromNameFg(context, colorIndexPlusOne - 1)
|
||||||
: stm->msgServiceFg->c);
|
: stm->msgServiceFg->c);
|
||||||
_name.drawLeftElided(p, x + textLeft, y + st::historyReplyPadding.top(), w, w + 2 * x + 2 * textLeft);
|
_name.drawLeftElided(p, x + textLeft, y + st::historyReplyPadding.top(), w, w + 2 * x + 2 * textLeft);
|
||||||
if (originalVia && w > _name.maxWidth() + st::msgServiceFont->spacew) {
|
|
||||||
p.setFont(st::msgServiceFont);
|
|
||||||
p.drawText(x + textLeft + _name.maxWidth() + st::msgServiceFont->spacew, y + st::historyReplyPadding.top() + st::msgServiceFont->ascent, originalVia->text);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.setPen(inBubble
|
p.setPen(inBubble
|
||||||
? stm->historyTextFg
|
? stm->historyTextFg
|
||||||
|
|
|
@ -276,8 +276,12 @@ struct HistoryMessageReply
|
||||||
|
|
||||||
[[nodiscard]] bool external() const;
|
[[nodiscard]] bool external() const;
|
||||||
[[nodiscard]] PeerData *sender(not_null<HistoryItem*> holder) const;
|
[[nodiscard]] PeerData *sender(not_null<HistoryItem*> holder) const;
|
||||||
[[nodiscard]] QString senderName(not_null<HistoryItem*> holder) const;
|
[[nodiscard]] QString senderName(
|
||||||
[[nodiscard]] QString senderName(not_null<PeerData*> peer) const;
|
not_null<HistoryItem*> holder,
|
||||||
|
bool shorten) const;
|
||||||
|
[[nodiscard]] QString senderName(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
bool shorten) const;
|
||||||
[[nodiscard]] bool isNameUpdated(not_null<HistoryItem*> holder) const;
|
[[nodiscard]] bool isNameUpdated(not_null<HistoryItem*> holder) const;
|
||||||
void updateName(
|
void updateName(
|
||||||
not_null<HistoryItem*> holder,
|
not_null<HistoryItem*> holder,
|
||||||
|
@ -340,7 +344,6 @@ struct HistoryMessageReply
|
||||||
WebPageId replyToWebPageId = 0;
|
WebPageId replyToWebPageId = 0;
|
||||||
ReplyToMessagePointer resolvedMessage;
|
ReplyToMessagePointer resolvedMessage;
|
||||||
ReplyToStoryPointer resolvedStory;
|
ReplyToStoryPointer resolvedStory;
|
||||||
std::unique_ptr<HistoryMessageVia> originalVia;
|
|
||||||
std::unique_ptr<Ui::SpoilerAnimation> spoiler;
|
std::unique_ptr<Ui::SpoilerAnimation> spoiler;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -349,6 +352,10 @@ struct HistoryMessageReply
|
||||||
} ripple;
|
} ripple;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] bool hasQuoteIcon() const;
|
||||||
|
[[nodiscard]] QSize countMultilineOptimalSize(
|
||||||
|
int firstLineSkip) const;
|
||||||
|
|
||||||
ReplyFields _fields;
|
ReplyFields _fields;
|
||||||
ClickHandlerPtr _link;
|
ClickHandlerPtr _link;
|
||||||
mutable Ui::Text::String _name;
|
mutable Ui::Text::String _name;
|
||||||
|
@ -359,6 +366,9 @@ private:
|
||||||
mutable int _height = 0;
|
mutable int _height = 0;
|
||||||
mutable int _nameVersion = 0;
|
mutable int _nameVersion = 0;
|
||||||
uint8 _unavailable : 1 = 0;
|
uint8 _unavailable : 1 = 0;
|
||||||
|
uint8 _displaying : 1 = 0;
|
||||||
|
uint8 _multiline : 1 = 0;
|
||||||
|
uint8 _expandable : 1 = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -771,13 +771,9 @@ QSize Message::performCountOptimalSize() {
|
||||||
accumulate_max(maxWidth, namew);
|
accumulate_max(maxWidth, namew);
|
||||||
}
|
}
|
||||||
if (reply) {
|
if (reply) {
|
||||||
auto replyw = st::msgPadding.left()
|
const auto replyw = st::msgPadding.left()
|
||||||
+ reply->maxWidth()
|
+ reply->maxWidth()
|
||||||
+ st::msgPadding.right();
|
+ st::msgPadding.right();
|
||||||
if (reply->originalVia) {
|
|
||||||
replyw += st::msgServiceFont->spacew
|
|
||||||
+ reply->originalVia->maxWidth;
|
|
||||||
}
|
|
||||||
accumulate_max(maxWidth, replyw);
|
accumulate_max(maxWidth, replyw);
|
||||||
}
|
}
|
||||||
if (entry) {
|
if (entry) {
|
||||||
|
|
|
@ -32,6 +32,12 @@ historyReplyBottom: 2px;
|
||||||
historyReplyPreview: 32px;
|
historyReplyPreview: 32px;
|
||||||
historyReplyPreviewMargin: margins(7px, 4px, 4px, 4px);
|
historyReplyPreviewMargin: margins(7px, 4px, 4px, 4px);
|
||||||
historyReplyPadding: margins(11px, 2px, 6px, 2px);
|
historyReplyPadding: margins(11px, 2px, 6px, 2px);
|
||||||
|
historyReplyUser: icon {{ "chat/reply_type_user", windowFg }};
|
||||||
|
historyReplyUserPadding: margins(0px, 4px, 4px, 0px);
|
||||||
|
historyReplyGroup: icon {{ "chat/reply_type_group", windowFg }};
|
||||||
|
historyReplyGroupPadding: margins(0px, 4px, 4px, 0px);
|
||||||
|
historyReplyChannel: icon {{ "chat/reply_type_channel", windowFg }};
|
||||||
|
historyReplyChannelPadding: margins(0px, 5px, 4px, 0px);
|
||||||
|
|
||||||
msgReplyPadding: margins(6px, 6px, 11px, 6px);
|
msgReplyPadding: margins(6px, 6px, 11px, 6px);
|
||||||
msgReplyBarPos: point(1px, 0px);
|
msgReplyBarPos: point(1px, 0px);
|
||||||
|
|