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);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString InternalPrefix() {
|
||||
return u"internal:"_q;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class CustomEmojiLoader final
|
||||
@ -514,6 +518,9 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||
Fn<void()> update,
|
||||
SizeTag tag,
|
||||
int sizeOverride) {
|
||||
if (data.startsWith(InternalPrefix())) {
|
||||
return internal(data);
|
||||
}
|
||||
const auto parsed = ParseCustomEmojiData(data);
|
||||
return parsed
|
||||
? 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(
|
||||
QStringView data,
|
||||
not_null<Listener*> listener) {
|
||||
@ -885,6 +904,34 @@ uint64 CustomEmojiManager::coloredSetId() const {
|
||||
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) {
|
||||
const auto emoji = EmojiSizeFromTag(tag);
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
|
@ -83,11 +83,22 @@ public:
|
||||
[[nodiscard]] Main::Session &session() 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;
|
||||
|
||||
private:
|
||||
static constexpr auto kSizeCount = int(SizeTag::kCount);
|
||||
|
||||
struct InternalEmojiData {
|
||||
QImage image;
|
||||
bool textColor = true;
|
||||
};
|
||||
struct RepaintBunch {
|
||||
crl::time when = 0;
|
||||
std::vector<base::weak_ptr<Ui::CustomEmoji::Instance>> instances;
|
||||
@ -131,6 +142,8 @@ private:
|
||||
SizeTag tag,
|
||||
int sizeOverride,
|
||||
LoaderFactory factory);
|
||||
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> internal(
|
||||
QStringView data);
|
||||
[[nodiscard]] static int SizeIndex(SizeTag tag);
|
||||
|
||||
const not_null<Session*> _owner;
|
||||
@ -163,6 +176,9 @@ private:
|
||||
bool _repaintTimerScheduled = false;
|
||||
bool _requestSetsScheduled = false;
|
||||
|
||||
std::vector<InternalEmojiData> _internalEmoji;
|
||||
base::flat_map<not_null<const style::icon*>, QString> _iconEmoji;
|
||||
|
||||
#if 0 // inject-to-on_main
|
||||
crl::time _repaintsLastAdded = 0;
|
||||
rpl::lifetime _repaintsLifetime;
|
||||
|
@ -477,7 +477,6 @@ HistoryMessageReply &HistoryMessageReply::operator=(
|
||||
HistoryMessageReply::~HistoryMessageReply() {
|
||||
// clearData() should be called by holder.
|
||||
Expects(resolvedMessage.empty());
|
||||
Expects(originalVia == nullptr);
|
||||
}
|
||||
|
||||
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();
|
||||
if (resolvedMessage
|
||||
_multiline = 0;
|
||||
// #TODO !_fields.storyId && (external || !_fields.quote.empty());
|
||||
|
||||
const auto displaying = resolvedMessage
|
||||
|| resolvedStory
|
||||
|| (external && (!_fields.messageId || force))) {
|
||||
const auto repaint = [=] { holder->customEmojiRepaint(); };
|
||||
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);
|
||||
|| (external && (!_fields.messageId || force));
|
||||
_displaying = displaying ? 1 : 0;
|
||||
|
||||
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);
|
||||
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
|
||||
? resolvedMessage->media()
|
||||
: nullptr;
|
||||
@ -642,7 +639,6 @@ void HistoryMessageReply::setTopMessageId(MsgId topMessageId) {
|
||||
}
|
||||
|
||||
void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
|
||||
originalVia = nullptr;
|
||||
if (resolvedMessage) {
|
||||
holder->history()->owner().unregisterDependentMessage(
|
||||
holder,
|
||||
@ -658,6 +654,12 @@ void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
|
||||
_name.clear();
|
||||
_text.clear();
|
||||
_unavailable = 1;
|
||||
_displaying = 0;
|
||||
_expandable = 0;
|
||||
if (_multiline) {
|
||||
holder->history()->owner().requestItemResize(holder);
|
||||
_multiline = 0;
|
||||
}
|
||||
refreshReplyToMedia();
|
||||
}
|
||||
|
||||
@ -691,9 +693,10 @@ PeerData *HistoryMessageReply::sender(not_null<HistoryItem*> holder) const {
|
||||
}
|
||||
|
||||
QString HistoryMessageReply::senderName(
|
||||
not_null<HistoryItem*> holder) const {
|
||||
not_null<HistoryItem*> holder,
|
||||
bool shorten) const {
|
||||
if (const auto peer = sender(holder)) {
|
||||
return senderName(peer);
|
||||
return senderName(peer, shorten);
|
||||
} else if (!resolvedMessage) {
|
||||
return _fields.externalSenderName;
|
||||
} else if (holder->Has<HistoryMessageForwarded>()) {
|
||||
@ -708,11 +711,11 @@ QString HistoryMessageReply::senderName(
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString HistoryMessageReply::senderName(not_null<PeerData*> peer) const {
|
||||
if (const auto user = originalVia ? peer->asUser() : nullptr) {
|
||||
return user->firstName;
|
||||
}
|
||||
return peer->name();
|
||||
QString HistoryMessageReply::senderName(
|
||||
not_null<PeerData*> peer,
|
||||
bool shorten) const {
|
||||
const auto user = shorten ? peer->asUser() : nullptr;
|
||||
return user ? user->firstName : peer->name();
|
||||
}
|
||||
|
||||
bool HistoryMessageReply::isNameUpdated(
|
||||
@ -729,47 +732,101 @@ bool HistoryMessageReply::isNameUpdated(
|
||||
void HistoryMessageReply::updateName(
|
||||
not_null<HistoryItem*> holder,
|
||||
std::optional<PeerData*> resolvedSender) const {
|
||||
const auto peer = resolvedSender.value_or(sender(holder));
|
||||
const auto name = peer ? senderName(peer) : senderName(holder);
|
||||
auto viaBotUsername = QString();
|
||||
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
|
||||
&& resolvedStory->hasReplyPreview())
|
||||
|| (resolvedMessage
|
||||
&& resolvedMessage->media()
|
||||
&& resolvedMessage->media()->hasReplyPreview());
|
||||
const auto textLeft = hasPreview
|
||||
? (st::messageQuoteStyle.outline
|
||||
+ st::historyReplyPreviewMargin.left()
|
||||
+ st::historyReplyPreview
|
||||
+ st::historyReplyPreviewMargin.right())
|
||||
: st::historyReplyPadding.left();
|
||||
if (!name.isEmpty()) {
|
||||
_name.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
|
||||
if (peer) {
|
||||
_nameVersion = peer->nameVersion();
|
||||
}
|
||||
const auto w = _name.maxWidth()
|
||||
+ (originalVia
|
||||
? (st::msgServiceFont->spacew + originalVia->maxWidth)
|
||||
: 0)
|
||||
+ (_fields.quote.empty()
|
||||
? 0
|
||||
: st::messageTextStyle.blockquote.icon.width());
|
||||
_maxWidth = std::max(
|
||||
w,
|
||||
std::min(_text.maxWidth(), st::maxSignatureSize))
|
||||
+ (_fields.storyId
|
||||
? (st::dialogsMiniReplyStory.skipText
|
||||
+ st::dialogsMiniReplyStory.icon.icon.width())
|
||||
: 0);
|
||||
} else {
|
||||
_maxWidth = st::msgDateFont->width(statePhrase());
|
||||
const auto previewSkip = st::messageQuoteStyle.outline
|
||||
+ st::historyReplyPreviewMargin.left()
|
||||
+ st::historyReplyPreview
|
||||
+ st::historyReplyPreviewMargin.right()
|
||||
- st::historyReplyPadding.left();
|
||||
const auto peerIcon = [](PeerData *peer) {
|
||||
return !peer
|
||||
? &st::historyReplyUser
|
||||
: peer->isBroadcast()
|
||||
? &st::historyReplyChannel
|
||||
: (peer->isChannel() || peer->isChat())
|
||||
? &st::historyReplyGroup
|
||||
: &st::historyReplyUser;
|
||||
};
|
||||
const auto peerEmoji = [&](PeerData *peer) {
|
||||
const auto owner = &holder->history()->owner();
|
||||
return Ui::Text::SingleCustomEmoji(
|
||||
owner->customEmojiManager().registerInternalEmoji(
|
||||
*peerIcon(peer)));
|
||||
};
|
||||
auto nameFull = TextWithEntities();
|
||||
if (!groupNameAdded && !_fields.storyId) {
|
||||
nameFull.append(peerEmoji(sender));
|
||||
}
|
||||
_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
|
||||
+ st::historyReplyPadding.right();
|
||||
_minHeight = st::historyReplyPadding.top()
|
||||
+ st::msgServiceNameFont->height
|
||||
+ st::normalFont->height
|
||||
+ optimalTextSize.height()
|
||||
+ st::historyReplyPadding.bottom();
|
||||
}
|
||||
|
||||
@ -785,17 +842,11 @@ int HistoryMessageReply::resizeToWidth(int width) const {
|
||||
+ st::historyReplyPreview
|
||||
+ st::historyReplyPreviewMargin.right())
|
||||
: st::historyReplyPadding.left();
|
||||
if (originalVia) {
|
||||
originalVia->resize(width
|
||||
- textLeft
|
||||
- st::historyReplyPadding.right()
|
||||
- _name.maxWidth()
|
||||
- st::msgServiceFont->spacew);
|
||||
}
|
||||
if (width >= _maxWidth) {
|
||||
if (width >= _maxWidth || !_multiline) {
|
||||
_height = _minHeight;
|
||||
return height();
|
||||
}
|
||||
// #TODO
|
||||
_height = _minHeight;
|
||||
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(
|
||||
Painter &p,
|
||||
not_null<const HistoryView::Element*> holder,
|
||||
@ -839,7 +899,7 @@ void HistoryMessageReply::paint(
|
||||
|
||||
y += st::historyReplyTop;
|
||||
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 colorPeer = resolvedMessage
|
||||
? resolvedMessage->displayFrom()
|
||||
@ -969,10 +1029,6 @@ void HistoryMessageReply::paint(
|
||||
? FromNameFg(context, colorIndexPlusOne - 1)
|
||||
: stm->msgServiceFg->c);
|
||||
_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
|
||||
? stm->historyTextFg
|
||||
|
@ -276,8 +276,12 @@ struct HistoryMessageReply
|
||||
|
||||
[[nodiscard]] bool external() const;
|
||||
[[nodiscard]] PeerData *sender(not_null<HistoryItem*> holder) const;
|
||||
[[nodiscard]] QString senderName(not_null<HistoryItem*> holder) const;
|
||||
[[nodiscard]] QString senderName(not_null<PeerData*> peer) const;
|
||||
[[nodiscard]] QString senderName(
|
||||
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;
|
||||
void updateName(
|
||||
not_null<HistoryItem*> holder,
|
||||
@ -340,7 +344,6 @@ struct HistoryMessageReply
|
||||
WebPageId replyToWebPageId = 0;
|
||||
ReplyToMessagePointer resolvedMessage;
|
||||
ReplyToStoryPointer resolvedStory;
|
||||
std::unique_ptr<HistoryMessageVia> originalVia;
|
||||
std::unique_ptr<Ui::SpoilerAnimation> spoiler;
|
||||
|
||||
struct {
|
||||
@ -349,6 +352,10 @@ struct HistoryMessageReply
|
||||
} ripple;
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool hasQuoteIcon() const;
|
||||
[[nodiscard]] QSize countMultilineOptimalSize(
|
||||
int firstLineSkip) const;
|
||||
|
||||
ReplyFields _fields;
|
||||
ClickHandlerPtr _link;
|
||||
mutable Ui::Text::String _name;
|
||||
@ -359,6 +366,9 @@ private:
|
||||
mutable int _height = 0;
|
||||
mutable int _nameVersion = 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);
|
||||
}
|
||||
if (reply) {
|
||||
auto replyw = st::msgPadding.left()
|
||||
const auto replyw = st::msgPadding.left()
|
||||
+ reply->maxWidth()
|
||||
+ st::msgPadding.right();
|
||||
if (reply->originalVia) {
|
||||
replyw += st::msgServiceFont->spacew
|
||||
+ reply->originalVia->maxWidth;
|
||||
}
|
||||
accumulate_max(maxWidth, replyw);
|
||||
}
|
||||
if (entry) {
|
||||
|
@ -32,6 +32,12 @@ historyReplyBottom: 2px;
|
||||
historyReplyPreview: 32px;
|
||||
historyReplyPreviewMargin: margins(7px, 4px, 4px, 4px);
|
||||
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);
|
||||
msgReplyBarPos: point(1px, 0px);
|
||||
|