diff --git a/src/timeline/TimelineItem.cc b/src/timeline/TimelineItem.cc
index bbe3ff34..7fc54962 100644
--- a/src/timeline/TimelineItem.cc
+++ b/src/timeline/TimelineItem.cc
@@ -24,6 +24,7 @@
#include "ChatPage.h"
#include "Config.h"
#include "Logging.hpp"
+#include "Painter.h"
#include "timeline/TimelineItem.h"
#include "timeline/widgets/AudioItem.h"
@@ -31,11 +32,90 @@
#include "timeline/widgets/ImageItem.h"
#include "timeline/widgets/VideoItem.h"
-constexpr const static char *CHECKMARK = "✓";
-
constexpr int MSG_RIGHT_MARGIN = 7;
constexpr int MSG_PADDING = 20;
+StatusIndicator::StatusIndicator(QWidget *parent)
+ : QWidget(parent)
+{
+ lockIcon_.addFile(":/icons/icons/ui/lock.png");
+ clockIcon_.addFile(":/icons/icons/ui/clock.png");
+ checkmarkIcon_.addFile(":/icons/icons/ui/checkmark.png");
+}
+
+void
+StatusIndicator::paintIcon(QPainter &p, QIcon &icon)
+{
+ auto pixmap = icon.pixmap(width());
+
+ QPainter painter(&pixmap);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ painter.fillRect(pixmap.rect(), p.pen().color());
+
+ QIcon(pixmap).paint(&p, rect(), Qt::AlignCenter, QIcon::Normal);
+}
+
+void
+StatusIndicator::paintEvent(QPaintEvent *)
+{
+ if (state_ == StatusIndicatorState::Empty)
+ return;
+
+ Painter p(this);
+ PainterHighQualityEnabler hq(p);
+
+ p.setPen(iconColor_);
+
+ switch (state_) {
+ case StatusIndicatorState::Sent: {
+ paintIcon(p, clockIcon_);
+ break;
+ }
+ case StatusIndicatorState::Encrypted:
+ paintIcon(p, lockIcon_);
+ break;
+ case StatusIndicatorState::Received: {
+ paintIcon(p, checkmarkIcon_);
+ break;
+ }
+ case StatusIndicatorState::Empty:
+ break;
+ }
+}
+
+void
+StatusIndicator::setState(StatusIndicatorState state)
+{
+ state_ = state;
+ update();
+}
+
+void
+TimelineItem::adjustMessageLayoutForWidget()
+{
+ messageLayout_->addLayout(widgetLayout_, 1);
+ messageLayout_->addWidget(statusIndicator_);
+ messageLayout_->addWidget(timestamp_);
+
+ messageLayout_->setAlignment(statusIndicator_, Qt::AlignTop);
+ messageLayout_->setAlignment(timestamp_, Qt::AlignTop);
+
+ mainLayout_->addLayout(messageLayout_);
+}
+
+void
+TimelineItem::adjustMessageLayout()
+{
+ messageLayout_->addWidget(body_, 1);
+ messageLayout_->addWidget(statusIndicator_);
+ messageLayout_->addWidget(timestamp_);
+
+ messageLayout_->setAlignment(statusIndicator_, Qt::AlignTop);
+ messageLayout_->setAlignment(timestamp_, Qt::AlignTop);
+
+ mainLayout_->addLayout(messageLayout_);
+}
+
void
TimelineItem::init()
{
@@ -102,14 +182,13 @@ TimelineItem::init()
mainLayout_->setContentsMargins(conf::timeline::headerLeftMargin, 0, 0, 0);
mainLayout_->setSpacing(0);
- QFont checkmarkFont;
- checkmarkFont.setPixelSize(conf::timeline::fonts::timestamp);
+ QFont timestampFont;
+ timestampFont.setPixelSize(conf::timeline::fonts::indicator);
+ QFontMetrics tsFm(timestampFont);
- // Setting fixed width for checkmark because systems may have a differing width for a
- // space and the Unicode checkmark.
- checkmark_ = new QLabel(this);
- checkmark_->setFont(checkmarkFont);
- checkmark_->setFixedWidth(QFontMetrics{checkmarkFont}.width(CHECKMARK));
+ statusIndicator_ = new StatusIndicator(this);
+ statusIndicator_->setFixedWidth(tsFm.height() - tsFm.leading());
+ statusIndicator_->setFixedHeight(tsFm.height() - tsFm.leading());
}
/*
@@ -147,20 +226,14 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
generateBody(userid, displayName, body);
setupAvatarLayout(displayName);
- messageLayout_->addLayout(headerLayout_, 1);
-
AvatarProvider::resolve(
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
} else {
generateBody(body);
setupSimpleLayout();
-
- messageLayout_->addWidget(body_, 1);
}
- messageLayout_->addWidget(checkmark_);
- messageLayout_->addWidget(timestamp_);
- mainLayout_->addLayout(messageLayout_);
+ adjustMessageLayout();
}
TimelineItem::TimelineItem(ImageItem *image,
@@ -316,20 +389,14 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
generateBody(sender, displayName, body);
setupAvatarLayout(displayName);
- messageLayout_->addLayout(headerLayout_, 1);
-
AvatarProvider::resolve(
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
} else {
generateBody(body);
setupSimpleLayout();
-
- messageLayout_->addWidget(body_, 1);
}
- messageLayout_->addWidget(checkmark_);
- messageLayout_->addWidget(timestamp_);
- mainLayout_->addLayout(messageLayout_);
+ adjustMessageLayout();
}
/*
@@ -364,20 +431,14 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
generateBody(sender, displayName, emoteMsg);
setupAvatarLayout(displayName);
- messageLayout_->addLayout(headerLayout_, 1);
-
AvatarProvider::resolve(
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
} else {
generateBody(emoteMsg);
setupSimpleLayout();
-
- messageLayout_->addWidget(body_, 1);
}
- messageLayout_->addWidget(checkmark_);
- messageLayout_->addWidget(timestamp_);
- mainLayout_->addLayout(messageLayout_);
+ adjustMessageLayout();
}
/*
@@ -417,28 +478,31 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
generateBody(sender, displayName, body);
setupAvatarLayout(displayName);
- messageLayout_->addLayout(headerLayout_, 1);
-
AvatarProvider::resolve(
room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
} else {
generateBody(body);
setupSimpleLayout();
-
- messageLayout_->addWidget(body_, 1);
}
- messageLayout_->addWidget(checkmark_);
- messageLayout_->addWidget(timestamp_);
- mainLayout_->addLayout(messageLayout_);
+ adjustMessageLayout();
}
void
-TimelineItem::markReceived()
+TimelineItem::markSent()
+{
+ statusIndicator_->setState(StatusIndicatorState::Sent);
+}
+
+void
+TimelineItem::markReceived(bool isEncrypted)
{
isReceived_ = true;
- checkmark_->setText(CHECKMARK);
- checkmark_->setAlignment(Qt::AlignTop);
+
+ if (isEncrypted)
+ statusIndicator_->setState(StatusIndicatorState::Encrypted);
+ else
+ statusIndicator_->setState(StatusIndicatorState::Received);
sendReadReceipt();
}
@@ -506,17 +570,10 @@ TimelineItem::generateTimestamp(const QDateTime &time)
QFont timestampFont;
timestampFont.setPixelSize(conf::timeline::fonts::timestamp);
- QFontMetrics fm(timestampFont);
- int topMargin = QFontMetrics(font_).ascent() - fm.ascent();
-
timestamp_ = new QLabel(this);
- timestamp_->setAlignment(Qt::AlignTop);
timestamp_->setFont(timestampFont);
timestamp_->setText(
QString("<span style=\"color: #999\"> %1 </span>").arg(time.toString("HH:mm")));
- timestamp_->setContentsMargins(0, topMargin, 0, 0);
- timestamp_->setStyleSheet(
- QString("font-size: %1px;").arg(conf::timeline::fonts::timestamp));
}
QString
@@ -557,15 +614,8 @@ TimelineItem::setupAvatarLayout(const QString &userName)
topLayout_->insertWidget(0, userAvatar_);
topLayout_->setAlignment(userAvatar_, Qt::AlignTop);
- headerLayout_ = new QVBoxLayout;
- headerLayout_->setMargin(0);
- headerLayout_->setSpacing(conf::timeline::headerSpacing);
-
if (userName_)
- headerLayout_->addWidget(userName_);
-
- if (body_)
- headerLayout_->addWidget(body_);
+ mainLayout_->insertWidget(0, userName_);
}
void
@@ -647,33 +697,8 @@ TimelineItem::addAvatar()
userName_->setFont(usernameFont_);
userName_->setText(fm.elidedText(displayName, Qt::ElideRight, 500));
- QWidget *widget = nullptr;
-
- // Extract the widget before we delete its layout.
- if (widgetLayout_)
- widget = widgetLayout_->itemAt(0)->widget();
-
- // Remove all items from the layout.
- QLayoutItem *item;
- while ((item = messageLayout_->takeAt(0)) != 0)
- delete item;
-
setupAvatarLayout(displayName);
- // Restore widget's layout.
- if (widget) {
- widgetLayout_ = new QHBoxLayout();
- widgetLayout_->setContentsMargins(0, 2, 0, 2);
- widgetLayout_->addWidget(widget);
- widgetLayout_->addStretch(1);
-
- headerLayout_->addLayout(widgetLayout_);
- }
-
- messageLayout_->addLayout(headerLayout_, 1);
- messageLayout_->addWidget(checkmark_);
- messageLayout_->addWidget(timestamp_);
-
AvatarProvider::resolve(
room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
}
|