mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
feat: service message time badge
This commit is contained in:
@@ -117,3 +117,10 @@ ghostPickerButton: LinkButton(defaultLinkButton) {
|
||||
}
|
||||
|
||||
ayuBetaBadgePadding: margins(4px, 1px, 4px, 1px);
|
||||
|
||||
ayuServiceTimeBadgeFont: font(11px semibold);
|
||||
ayuServiceTimeBadgeHorizontalPadding: 7px;
|
||||
ayuServiceTimeBadgeVerticalPadding: 2px;
|
||||
ayuServiceTimeBadgeGap: 8px;
|
||||
ayuServiceTimeBadgeTrailingInset: 3px;
|
||||
ayuServiceTimeBadgeLastLineWidthAdd: 9px;
|
||||
|
||||
@@ -1367,20 +1367,11 @@ void HistoryItem::setCommentsItemId(FullMsgId id) {
|
||||
}
|
||||
|
||||
void HistoryItem::setServiceText(PreparedServiceText &&prepared) {
|
||||
auto text = std::move(prepared.text);
|
||||
|
||||
if (date() > 0) {
|
||||
const auto timeString = QString(" (%1)").arg(formatMessageTime(base::unixtime::parse(_date).time()));
|
||||
if (!text.text.isEmpty() && !text.text.contains(timeString)) {
|
||||
text = text.append(timeString);
|
||||
}
|
||||
}
|
||||
|
||||
AddComponents(HistoryServiceData::Bit());
|
||||
_flags &= ~MessageFlag::HasTextLinks;
|
||||
const auto data = Get<HistoryServiceData>();
|
||||
const auto had = !_text.empty();
|
||||
_text = std::move(text);
|
||||
_text = std::move(prepared.text);
|
||||
data->textLinks = std::move(prepared.links);
|
||||
if (had) {
|
||||
_history->owner().requestItemTextRefresh(this);
|
||||
|
||||
@@ -32,6 +32,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
// AyuGram includes
|
||||
#include "ayu/utils/telegram_helpers.h"
|
||||
#include "styles/style_ayu_styles.h"
|
||||
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
@@ -200,6 +205,64 @@ void SetText(Ui::Text::String &text, const QString &content) {
|
||||
text.setText(st::serviceTextStyle, content, EmptyLineOptions);
|
||||
}
|
||||
|
||||
struct ServiceTimeMetrics {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int additionalWidth = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] ServiceTimeMetrics ComputeServiceTimeMetrics(const QString &text) {
|
||||
auto result = ServiceTimeMetrics();
|
||||
if (text.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
const auto &font = st::ayuServiceTimeBadgeFont;
|
||||
result.width = font->width(text)
|
||||
+ 2 * st::ayuServiceTimeBadgeHorizontalPadding;
|
||||
result.height = font->height + 2 * st::ayuServiceTimeBadgeVerticalPadding;
|
||||
result.additionalWidth = std::max(
|
||||
st::ayuServiceTimeBadgeGap
|
||||
+ result.width
|
||||
+ st::ayuServiceTimeBadgeTrailingInset
|
||||
- st::msgServicePadding.right(),
|
||||
0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void PaintServiceTime(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
int left,
|
||||
int top,
|
||||
const QString &text,
|
||||
const ServiceTimeMetrics &metrics) {
|
||||
if (text.isEmpty() || !metrics.width) {
|
||||
return;
|
||||
}
|
||||
const auto rect = QRect(
|
||||
left,
|
||||
top,
|
||||
metrics.width,
|
||||
metrics.height);
|
||||
const auto bg = QColor(0x00, 0x00, 0x00, 0x1e);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(bg);
|
||||
const auto radius = rect.height() / 2.;
|
||||
p.drawRoundedRect(
|
||||
rect,
|
||||
radius,
|
||||
radius);
|
||||
p.setPen(st->msgServiceFg());
|
||||
p.setFont(st::ayuServiceTimeBadgeFont);
|
||||
p.drawText(
|
||||
rect.x() + st::ayuServiceTimeBadgeHorizontalPadding,
|
||||
rect.y()
|
||||
+ st::ayuServiceTimeBadgeVerticalPadding
|
||||
+ st::ayuServiceTimeBadgeFont->ascent,
|
||||
text);
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::BubbleRounding KeyboardRounding() {
|
||||
return Ui::BubbleRounding{
|
||||
.topLeft = Ui::BubbleCornerRounding::Large,
|
||||
@@ -311,8 +374,9 @@ void ServiceMessagePainter::PaintComplexBubble(
|
||||
int left,
|
||||
int width,
|
||||
const Ui::Text::String &text,
|
||||
const QRect &textRect) {
|
||||
const auto lineWidths = CountLineWidths(text, textRect);
|
||||
const QRect &textRect,
|
||||
int lastLineWidthAdd) {
|
||||
const auto lineWidths = CountLineWidths(text, textRect, lastLineWidthAdd);
|
||||
|
||||
int y = st::msgServiceMargin.top(), previousRichWidth = 0;
|
||||
bool previousShrink = false, forceShrink = false;
|
||||
@@ -372,7 +436,8 @@ void ServiceMessagePainter::PaintComplexBubble(
|
||||
|
||||
std::vector<int> ServiceMessagePainter::CountLineWidths(
|
||||
const Ui::Text::String &text,
|
||||
const QRect &textRect) {
|
||||
const QRect &textRect,
|
||||
int lastLineWidthAdd) {
|
||||
const auto linesCount = qMax(
|
||||
textRect.height() / st::msgServiceFont->height,
|
||||
1);
|
||||
@@ -409,6 +474,12 @@ std::vector<int> ServiceMessagePainter::CountLineWidths(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastLineWidthAdd > 0
|
||||
&& result.size() > 1
|
||||
&& result.back() > 0
|
||||
&& result.back() < result[result.size() - 2]) {
|
||||
result.back() += lastLineWidthAdd;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -445,6 +516,18 @@ bool Service::consumeHorizontalScroll(QPoint position, int delta) {
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString ServiceTimeText(
|
||||
const Ui::Text::String &text,
|
||||
const QDateTime &dateTime) {
|
||||
return (!text.isEmpty() && dateTime.isValid())
|
||||
? formatMessageTime(dateTime.time())
|
||||
: QString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QRect Service::countGeometry() const {
|
||||
auto result = QRect(0, 0, width(), height());
|
||||
if (delegate()->elementChatMode() == ElementChatMode::Wide) {
|
||||
@@ -485,6 +568,9 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
||||
}
|
||||
const auto media = this->media();
|
||||
const auto mediaDisplayed = media && media->isDisplayed();
|
||||
const auto hideText = mediaDisplayed && media->hideServiceText();
|
||||
const auto timeText = hideText ? QString() : ServiceTimeText(text(), dateTime());
|
||||
const auto timeMetrics = ComputeServiceTimeMetrics(timeText);
|
||||
auto contentWidth = newWidth;
|
||||
if (delegate()->elementChatMode() == ElementChatMode::Wide) {
|
||||
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||
@@ -493,10 +579,14 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
||||
if (contentWidth < st::msgServicePadding.left() + st::msgServicePadding.right() + 1) {
|
||||
contentWidth = st::msgServicePadding.left() + st::msgServicePadding.right() + 1;
|
||||
}
|
||||
if (mediaDisplayed && media->hideServiceText()) {
|
||||
const auto nwidth = qMax(contentWidth - st::msgServicePadding.left() - st::msgServicePadding.right(), 0);
|
||||
validateTextSkipBlock(
|
||||
!timeText.isEmpty(),
|
||||
timeMetrics.additionalWidth,
|
||||
qMax(timeMetrics.height, st::msgServiceFont->height));
|
||||
if (hideText) {
|
||||
newHeight += media->resizeGetHeight(newWidth) + marginBottom();
|
||||
} else if (!text().isEmpty()) {
|
||||
auto nwidth = qMax(contentWidth - st::msgServicePadding.left() - st::msgServicePadding.right(), 0);
|
||||
newHeight += (contentWidth >= maxWidth())
|
||||
? minHeight()
|
||||
: textHeightFor(nwidth);
|
||||
@@ -533,7 +623,6 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
||||
QSize Service::performCountOptimalSize() {
|
||||
const auto markup = data()->inlineReplyMarkup();
|
||||
validateText();
|
||||
validateInlineKeyboard(markup);
|
||||
|
||||
if (_reactions) {
|
||||
_reactions->initDimensions();
|
||||
@@ -542,9 +631,18 @@ QSize Service::performCountOptimalSize() {
|
||||
if (const auto media = this->media()) {
|
||||
media->initDimensions();
|
||||
if (media->hideServiceText()) {
|
||||
validateTextSkipBlock(false, 0, 0);
|
||||
validateInlineKeyboard(markup);
|
||||
return { media->maxWidth(), media->minHeight() };
|
||||
}
|
||||
}
|
||||
const auto timeText = ServiceTimeText(text(), dateTime());
|
||||
const auto timeMetrics = ComputeServiceTimeMetrics(timeText);
|
||||
validateTextSkipBlock(
|
||||
!timeText.isEmpty(),
|
||||
timeMetrics.additionalWidth,
|
||||
qMax(timeMetrics.height, st::msgServiceFont->height));
|
||||
validateInlineKeyboard(markup);
|
||||
auto maxWidth = text().maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||
auto minHeight = text().minHeight();
|
||||
return { maxWidth, minHeight };
|
||||
@@ -623,6 +721,8 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||
const auto media = this->media();
|
||||
const auto mediaDisplayed = media && media->isDisplayed();
|
||||
const auto onlyMedia = (mediaDisplayed && media->hideServiceText());
|
||||
const auto timeText = onlyMedia ? QString() : ServiceTimeText(text(), dateTime());
|
||||
const auto timeMetrics = ComputeServiceTimeMetrics(timeText);
|
||||
|
||||
if (_reactions) {
|
||||
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||
@@ -665,6 +765,15 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||
const auto mediaSkip = mediaDisplayed ? (st::msgServiceMargin.top() + media->height()) : 0;
|
||||
const auto trect = QRect(g.left(), g.top(), g.width(), g.height() - mediaSkip)
|
||||
- st::msgServicePadding;
|
||||
auto lineWidths = text().countLineWidths(trect.width());
|
||||
for (auto &lineWidth : lineWidths) {
|
||||
lineWidth = qMax(lineWidth, 0);
|
||||
}
|
||||
const auto lastLineWidthAdd = (lineWidths.size() > 1
|
||||
&& lineWidths.back() > timeMetrics.additionalWidth
|
||||
&& lineWidths.back() < lineWidths[lineWidths.size() - 2])
|
||||
? st::ayuServiceTimeBadgeLastLineWidthAdd
|
||||
: 0;
|
||||
|
||||
p.translate(0, g.top() - st::msgServiceMargin.top());
|
||||
ServiceMessagePainter::PaintComplexBubble(
|
||||
@@ -673,7 +782,8 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||
g.left(),
|
||||
g.width(),
|
||||
text(),
|
||||
trect);
|
||||
trect,
|
||||
lastLineWidthAdd);
|
||||
p.translate(0, -g.top() + st::msgServiceMargin.top());
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
@@ -692,6 +802,27 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||
.fullWidthSelection = false,
|
||||
.selection = context.selection,
|
||||
});
|
||||
|
||||
if (!timeText.isEmpty() && !lineWidths.empty()) {
|
||||
const auto lastLineWidth = lineWidths.back();
|
||||
const auto lastTextWidth = qMax(
|
||||
lastLineWidth - timeMetrics.additionalWidth,
|
||||
0);
|
||||
const auto gap = lastTextWidth ? st::ayuServiceTimeBadgeGap : 0;
|
||||
const auto lastLineTop = trect.y()
|
||||
+ (int(lineWidths.size()) - 1) * st::msgServiceFont->height;
|
||||
PaintServiceTime(
|
||||
p,
|
||||
context.st,
|
||||
trect.x()
|
||||
+ ((trect.width() - lastLineWidth) / 2)
|
||||
+ lastTextWidth
|
||||
+ gap,
|
||||
lastLineTop
|
||||
+ ((st::msgServiceFont->height - timeMetrics.height) / 2),
|
||||
timeText,
|
||||
timeMetrics);
|
||||
}
|
||||
}
|
||||
if (mediaDisplayed) {
|
||||
const auto left = g.left() + (g.width() - media->width()) / 2;
|
||||
|
||||
@@ -121,12 +121,14 @@ public:
|
||||
int left,
|
||||
int width,
|
||||
const Ui::Text::String &text,
|
||||
const QRect &textRect);
|
||||
const QRect &textRect,
|
||||
int lastLineWidthAdd = 0);
|
||||
|
||||
private:
|
||||
static std::vector<int> CountLineWidths(
|
||||
const Ui::Text::String &text,
|
||||
const QRect &textRect);
|
||||
const QRect &textRect,
|
||||
int lastLineWidthAdd = 0);
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user