diff --git a/CMakeLists.txt b/CMakeLists.txt
index 65ee0da7..495f80ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -170,6 +170,7 @@ set(SRC_FILES
src/ui/Avatar.cc
src/ui/Badge.cc
src/ui/LoadingIndicator.cc
+ src/ui/InfoMessage.cpp
src/ui/FlatButton.cc
src/ui/FloatingButton.cc
src/ui/Label.cc
@@ -283,6 +284,7 @@ qt5_wrap_cpp(MOC_HEADERS
include/ui/Avatar.h
include/ui/Badge.h
include/ui/LoadingIndicator.h
+ include/ui/InfoMessage.hpp
include/ui/FlatButton.h
include/ui/Label.h
include/ui/FloatingButton.h
diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h
index 81212c27..5af359c5 100644
--- a/include/timeline/TimelineView.h
+++ b/include/timeline/TimelineView.h
@@ -107,40 +107,6 @@ enum class TimelineDirection
Bottom,
};
-class DateSeparator : public QWidget
-{
- Q_OBJECT
-
- Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
- Q_PROPERTY(QColor boxColor WRITE setBoxColor READ boxColor)
-
-public:
- DateSeparator(QDateTime datetime, QWidget *parent = nullptr);
-
- void setTextColor(QColor color) { textColor_ = color; }
- void setBoxColor(QColor color) { boxColor_ = color; }
-
- QColor textColor() const { return textColor_; }
- QColor boxColor() const { return boxColor_; }
-
-protected:
- void paintEvent(QPaintEvent *event) override;
-
-private:
- static constexpr int VPadding = 6;
- static constexpr int HPadding = 12;
- static constexpr int HMargin = 20;
-
- int width_;
- int height_;
-
- QString msg_;
- QFont font_;
-
- QColor textColor_ = QColor("black");
- QColor boxColor_ = QColor("white");
-};
-
class TimelineView : public QWidget
{
Q_OBJECT
@@ -162,7 +128,6 @@ public:
uint64_t size);
void updatePendingMessage(const std::string &txn_id, const QString &event_id);
void scrollDown();
- QLabel *createDateSeparator(QDateTime datetime);
//! Remove an item from the timeline with the given Event ID.
void removeEvent(const QString &event_id);
@@ -220,7 +185,7 @@ private:
void getMessages();
//! HACK: Fixing layout flickering when adding to the bottom
//! of the timeline.
- void pushTimelineItem(TimelineItem *item)
+ void pushTimelineItem(QWidget *item)
{
item->hide();
scroll_layout_->addWidget(item);
@@ -230,7 +195,7 @@ private:
//! Decides whether or not to show or hide the scroll down button.
void toggleScrollDownButton();
void init();
- void addTimelineItem(TimelineItem *item,
+ void addTimelineItem(QWidget *item,
TimelineDirection direction = TimelineDirection::Bottom);
void updateLastSender(const QString &user_id, TimelineDirection direction);
void notifyForLastEvent();
@@ -295,8 +260,8 @@ private:
const QDateTime &second = QDateTime::currentDateTime()) const;
// Return nullptr if the event couldn't be parsed.
- TimelineItem *parseMessageEvent(const mtx::events::collections::TimelineEvents &event,
- TimelineDirection direction);
+ QWidget *parseMessageEvent(const mtx::events::collections::TimelineEvents &event,
+ TimelineDirection direction);
QVBoxLayout *top_layout_;
QVBoxLayout *scroll_layout_;
diff --git a/include/ui/InfoMessage.hpp b/include/ui/InfoMessage.hpp
new file mode 100644
index 00000000..58f98b0c
--- /dev/null
+++ b/include/ui/InfoMessage.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <QColor>
+#include <QDateTime>
+#include <QWidget>
+
+class InfoMessage : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
+ Q_PROPERTY(QColor boxColor WRITE setBoxColor READ boxColor)
+
+public:
+ explicit InfoMessage(QWidget *parent = nullptr);
+ InfoMessage(QString msg, QWidget *parent = nullptr);
+
+ void setTextColor(QColor color) { textColor_ = color; }
+ void setBoxColor(QColor color) { boxColor_ = color; }
+ void saveDatetime(QDateTime datetime) { datetime_ = datetime; }
+
+ QColor textColor() const { return textColor_; }
+ QColor boxColor() const { return boxColor_; }
+ QDateTime datetime() const { return datetime_; }
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+
+ int width_;
+ int height_;
+
+ QString msg_;
+ QFont font_;
+
+ QDateTime datetime_;
+
+ QColor textColor_ = QColor("black");
+ QColor boxColor_ = QColor("white");
+};
+
+class DateSeparator : public InfoMessage
+{
+ Q_OBJECT
+
+public:
+ DateSeparator(QDateTime datetime, QWidget *parent = nullptr);
+};
diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss
index 2e456d90..c95eca2f 100644
--- a/resources/styles/nheko-dark.qss
+++ b/resources/styles/nheko-dark.qss
@@ -23,7 +23,7 @@ QuickSwitcher {
background-color: #202228;
}
-DateSeparator {
+InfoMessage {
qproperty-textColor: #caccd1;
qproperty-boxColor: rgba(45, 49, 57, 120);
}
diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss
index 1cba4c82..674d1722 100644
--- a/resources/styles/nheko.qss
+++ b/resources/styles/nheko.qss
@@ -23,7 +23,7 @@ QuickSwitcher {
background-color: white;
}
-DateSeparator {
+InfoMessage {
qproperty-textColor: #333;
qproperty-boxColor: rgba(220, 220, 220, 120);
}
diff --git a/resources/styles/system.qss b/resources/styles/system.qss
index 0921a832..e18b7e5c 100644
--- a/resources/styles/system.qss
+++ b/resources/styles/system.qss
@@ -25,7 +25,7 @@ QuickSwitcher {
background-color: palette(window);
}
-DateSeparator {
+InfoMessage {
qproperty-textColor: palette(text);
qproperty-boxColor: palette(window);
}
diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc
index e9c5b017..35140df0 100644
--- a/src/timeline/TimelineView.cc
+++ b/src/timeline/TimelineView.cc
@@ -23,6 +23,7 @@
#include "ChatPage.h"
#include "Config.h"
#include "FloatingButton.h"
+#include "InfoMessage.hpp"
#include "Logging.hpp"
#include "Olm.hpp"
#include "UserSettingsPage.h"
@@ -36,55 +37,19 @@
using TimelineEvent = mtx::events::collections::TimelineEvents;
-DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent)
- : QWidget{parent}
+//! Retrieve the timestamp of the event represented by the given widget.
+QDateTime
+getDate(QWidget *widget)
{
- auto now = QDateTime::currentDateTime();
- auto days = now.daysTo(datetime);
+ auto item = qobject_cast<TimelineItem *>(widget);
+ if (item)
+ return item->descriptionMessage().datetime;
- font_.setWeight(60);
- font_.setPixelSize(conf::timeline::fonts::dateSeparator);
+ auto infoMsg = qobject_cast<InfoMessage *>(widget);
+ if (infoMsg)
+ return infoMsg->datetime();
- QString fmt;
-
- if (now.date().year() != datetime.date().year())
- fmt = QString("ddd d MMMM yy");
- else
- fmt = QString("ddd d MMMM");
-
- if (days == 0)
- msg_ = tr("Today");
- else if (std::abs(days) == 1)
- msg_ = tr("Yesterday");
- else
- msg_ = datetime.toString(fmt);
-
- QFontMetrics fm{font_};
- width_ = fm.width(msg_) + HPadding * 2;
- height_ = fm.ascent() + 2 * VPadding;
-
- setFixedHeight(height_ + 2 * HMargin);
-}
-
-void
-DateSeparator::paintEvent(QPaintEvent *)
-{
- QPainter p(this);
- p.setRenderHint(QPainter::Antialiasing);
- p.setFont(font_);
-
- // Center the box horizontally & vertically.
- auto textRegion = QRectF(width() / 2 - width_ / 2, HMargin, width_, height_);
-
- QPainterPath ppath;
- ppath.addRoundedRect(textRegion, height_ / 2, height_ / 2);
-
- p.setPen(Qt::NoPen);
- p.fillPath(ppath, boxColor());
- p.drawPath(ppath);
-
- p.setPen(QPen(textColor()));
- p.drawText(textRegion, Qt::AlignCenter, msg_);
+ return QDateTime();
}
TimelineView::TimelineView(const mtx::responses::Timeline &timeline,
@@ -231,7 +196,7 @@ TimelineView::addBackwardsEvents(const mtx::responses::Messages &msgs)
isPaginationInProgress_ = false;
}
-TimelineItem *
+QWidget *
TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &event,
TimelineDirection direction)
{
@@ -255,6 +220,12 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &
});
return nullptr;
+ } else if (mpark::holds_alternative<StateEvent<state::Encryption>>(event)) {
+ auto msg = mpark::get<StateEvent<state::Encryption>>(event);
+ auto item = new InfoMessage(tr("Encryption is enabled"), this);
+ item->saveDatetime(QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts));
+
+ return item;
} else if (mpark::holds_alternative<RoomEvent<msg::Audio>>(event)) {
auto audio = mpark::get<RoomEvent<msg::Audio>>(event);
return processMessageEvent<AudioEvent, AudioItem>(audio, direction);
@@ -280,13 +251,18 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &
return processMessageEvent<Sticker, StickerItem>(mpark::get<Sticker>(event),
direction);
} else if (mpark::holds_alternative<EncryptedEvent<msg::Encrypted>>(event)) {
- auto res = parseEncryptedEvent(mpark::get<EncryptedEvent<msg::Encrypted>>(event));
- auto item = parseMessageEvent(res.event, direction);
+ auto res = parseEncryptedEvent(mpark::get<EncryptedEvent<msg::Encrypted>>(event));
+ auto widget = parseMessageEvent(res.event, direction);
- if (item != nullptr && res.isDecrypted)
+ if (widget == nullptr)
+ return nullptr;
+
+ auto item = qobject_cast<TimelineItem *>(widget);
+
+ if (item && res.isDecrypted)
item->markReceived(true);
- return item;
+ return widget;
}
return nullptr;
@@ -374,7 +350,7 @@ TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events)
int counter = 0;
for (const auto &event : events) {
- TimelineItem *item = parseMessageEvent(event, TimelineDirection::Bottom);
+ QWidget *item = parseMessageEvent(event, TimelineDirection::Bottom);
if (item != nullptr) {
addTimelineItem(item, TimelineDirection::Bottom);
@@ -395,7 +371,7 @@ TimelineView::renderBottomEvents(const std::vector<TimelineEvent> &events)
void
TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events)
{
- std::vector<TimelineItem *> items;
+ std::vector<QWidget *> items;
// Reset the sender of the first message in the timeline
// cause we're about to insert a new one.
@@ -408,7 +384,7 @@ TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events)
while (ii != 0) {
--ii;
- TimelineItem *item = parseMessageEvent(events[ii], TimelineDirection::Top);
+ auto item = parseMessageEvent(events[ii], TimelineDirection::Top);
if (item != nullptr)
items.push_back(item);
@@ -429,9 +405,16 @@ TimelineView::renderTopEvents(const std::vector<TimelineEvent> &events)
// If this batch is the first being rendered (i.e the first and the last
// events originate from this batch), set the last sender.
- if (lastSender_.isEmpty() && !items.empty())
- saveLastMessageInfo(items.at(0)->descriptionMessage().userid,
- items.at(0)->descriptionMessage().datetime);
+ if (lastSender_.isEmpty() && !items.empty()) {
+ for (const auto &w : items) {
+ auto timelineItem = qobject_cast<TimelineItem *>(w);
+ if (timelineItem) {
+ saveLastMessageInfo(timelineItem->descriptionMessage().userid,
+ timelineItem->descriptionMessage().datetime);
+ break;
+ }
+ }
+ }
}
void
@@ -569,17 +552,16 @@ TimelineView::isSenderRendered(const QString &user_id,
}
void
-TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction)
+TimelineView::addTimelineItem(QWidget *item, TimelineDirection direction)
{
- const auto newDate = item->descriptionMessage().datetime;
+ const auto newDate = getDate(item);
if (direction == TimelineDirection::Bottom) {
const auto lastItemPosition = scroll_layout_->count() - 1;
- auto lastItem =
- qobject_cast<TimelineItem *>(scroll_layout_->itemAt(lastItemPosition)->widget());
+ const auto lastItem = scroll_layout_->itemAt(lastItemPosition)->widget();
if (lastItem) {
- auto oldDate = lastItem->descriptionMessage().datetime;
+ const auto oldDate = getDate(lastItem);
if (oldDate.daysTo(newDate) != 0) {
auto separator = new DateSeparator(newDate, this);
@@ -594,11 +576,10 @@ TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction)
// The first item (position 0) is a stretch widget that pushes
// the widgets to the bottom of the page.
if (scroll_layout_->count() > 1) {
- auto firstItem =
- qobject_cast<TimelineItem *>(scroll_layout_->itemAt(1)->widget());
+ const auto firstItem = scroll_layout_->itemAt(1)->widget();
if (firstItem) {
- auto oldDate = firstItem->descriptionMessage().datetime;
+ const auto oldDate = getDate(firstItem);
if (newDate.daysTo(oldDate) != 0) {
auto separator = new DateSeparator(oldDate);
diff --git a/src/ui/InfoMessage.cpp b/src/ui/InfoMessage.cpp
new file mode 100644
index 00000000..b150e61b
--- /dev/null
+++ b/src/ui/InfoMessage.cpp
@@ -0,0 +1,79 @@
+#include "Config.h"
+#include "InfoMessage.hpp"
+
+#include <QDateTime>
+#include <QPainter>
+#include <QPen>
+
+constexpr int VPadding = 6;
+constexpr int HPadding = 12;
+constexpr int HMargin = 20;
+
+InfoMessage::InfoMessage(QWidget *parent)
+ : QWidget{parent}
+{
+ font_.setWeight(60);
+ font_.setPixelSize(conf::timeline::fonts::dateSeparator);
+}
+
+InfoMessage::InfoMessage(QString msg, QWidget *parent)
+ : QWidget{parent}
+ , msg_{msg}
+{
+ font_.setWeight(60);
+ font_.setPixelSize(conf::timeline::fonts::dateSeparator);
+
+ QFontMetrics fm{font_};
+ width_ = fm.width(msg_) + HPadding * 2;
+ height_ = fm.ascent() + 2 * VPadding;
+
+ setFixedHeight(height_ + 2 * HMargin);
+}
+
+void
+InfoMessage::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.setFont(font_);
+
+ // Center the box horizontally & vertically.
+ auto textRegion = QRectF(width() / 2 - width_ / 2, HMargin, width_, height_);
+
+ QPainterPath ppath;
+ ppath.addRoundedRect(textRegion, height_ / 2, height_ / 2);
+
+ p.setPen(Qt::NoPen);
+ p.fillPath(ppath, boxColor());
+ p.drawPath(ppath);
+
+ p.setPen(QPen(textColor()));
+ p.drawText(textRegion, Qt::AlignCenter, msg_);
+}
+
+DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent)
+ : InfoMessage{parent}
+{
+ auto now = QDateTime::currentDateTime();
+ auto days = now.daysTo(datetime);
+
+ QString fmt;
+
+ if (now.date().year() != datetime.date().year())
+ fmt = QString("ddd d MMMM yy");
+ else
+ fmt = QString("ddd d MMMM");
+
+ if (days == 0)
+ msg_ = tr("Today");
+ else if (std::abs(days) == 1)
+ msg_ = tr("Yesterday");
+ else
+ msg_ = datetime.toString(fmt);
+
+ QFontMetrics fm{font_};
+ width_ = fm.width(msg_) + HPadding * 2;
+ height_ = fm.ascent() + 2 * VPadding;
+
+ setFixedHeight(height_ + 2 * HMargin);
+}
|