From 579bf234606a8974a457f244195d2b4f3594d294 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Tue, 16 Jul 2019 22:36:55 -0400 Subject: [PATCH] Add User Mentions Dialog Add a RoomListItem-like button that opens a dialog containing all of the messages that would result in a highlight from the server (for example, the user is mentioned, or @room is mentioned). This is VERY rudimentary and will be completely reworked in the future to take advantage of the existing TimelineView class, instead of using a dialog like it does now. The button to show the mentions also needs work. --- CMakeLists.txt | 4 + deps/CMakeLists.txt | 4 +- nheko-backtrace.dump | Bin 288 -> 0 bytes resources/styles/nheko-dark.qss | 29 +++ src/ChatPage.cpp | 52 ++++++ src/ChatPage.h | 8 + src/UserMentionsWidget.cpp | 308 ++++++++++++++++++++++++++++++++ src/UserMentionsWidget.h | 164 +++++++++++++++++ src/dialogs/UserMentions.cpp | 59 ++++++ src/dialogs/UserMentions.h | 26 +++ 10 files changed, 652 insertions(+), 2 deletions(-) delete mode 100644 nheko-backtrace.dump create mode 100644 src/UserMentionsWidget.cpp create mode 100644 src/UserMentionsWidget.h create mode 100644 src/dialogs/UserMentions.cpp create mode 100644 src/dialogs/UserMentions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a0bf50f..09eea071 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,6 +179,7 @@ set(SRC_FILES src/dialogs/LeaveRoom.cpp src/dialogs/Logout.cpp src/dialogs/UserProfile.cpp + src/dialogs/UserMentions.cpp src/dialogs/ReadReceipts.cpp src/dialogs/ReCaptcha.cpp src/dialogs/RoomSettings.cpp @@ -246,6 +247,7 @@ set(SRC_FILES src/TypingDisplay.cpp src/Utils.cpp src/UserInfoWidget.cpp + src/UserMentionsWidget.cpp src/UserSettingsPage.cpp src/WelcomePage.cpp src/main.cpp @@ -319,6 +321,7 @@ qt5_wrap_cpp(MOC_HEADERS src/dialogs/MemberList.h src/dialogs/LeaveRoom.h src/dialogs/Logout.h + src/dialogs/UserMentions.h src/dialogs/UserProfile.h src/dialogs/RawMessage.h src/dialogs/ReadReceipts.h @@ -385,6 +388,7 @@ qt5_wrap_cpp(MOC_HEADERS src/TrayIcon.h src/TypingDisplay.h src/UserInfoWidget.h + src/UserMentionsWidget.h src/UserSettingsPage.h src/WelcomePage.h ) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 1df09cc9..dbc96463 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -46,10 +46,10 @@ set(BOOST_SHA256 set( MTXCLIENT_URL - https://github.com/Nheko-Reborn/mtxclient/archive/35b596a98d516e044a6a25803ba6b93b6c0a538b.tar.gz + https://github.com/Nheko-Reborn/mtxclient/archive/37df82363c800b8d6b2b172a486e395e4132e061.tar.gz ) set(MTXCLIENT_HASH - ea770f52afaad45706b8050aa3d860fa98780c60f2d3f061f2c89dfd3b3bf9be) + b29dd0bc836b69bd4253499519b8396a210bba43750fb66f4ffb8453510c8dd1) set( TWEENY_URL https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz diff --git a/nheko-backtrace.dump b/nheko-backtrace.dump deleted file mode 100644 index 11aeb9d28924fa25ad715dec0b57248fd7936947..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 288 zcmezHz~tucPzHtv>S8w&>KPbZU+aVE-)Q0tZl`a6)G>HL=nD(A!1|0`AoSHR4KUyT zo;sNBSbhH%$h>@wS716c6hbe0{2k0+GWRx^wo$$VrhoP+gK4MkhbZy~g;C{suU3QQ z)l_T1bj;OCFkQ2H&S{W+{E@xaKy+pWgkA@wD<5cp)$OT**yHvGLK}*kg5CT0oe0?9 NefCKZe*(P<0svAgasB`R diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss index c55960f9..7b5486b0 100644 --- a/resources/styles/nheko-dark.qss +++ b/resources/styles/nheko-dark.qss @@ -122,6 +122,35 @@ RoomInfoListItem { qproperty-bubbleBgColor: #4d84c7; } +UserMentionsWidget { + qproperty-mentionedColor: #a82353; + qproperty-highlightedBackgroundColor: #4d84c7; + qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30); + qproperty-backgroundColor: #2d3139; + + qproperty-titleColor: #e4e5e8; + qproperty-subtitleColor: #caccd1; + + qproperty-hoverTitleColor: #f4f5f8; + qproperty-hoverSubtitleColor: white; + + qproperty-highlightedTitleColor: #f4f4f5; + qproperty-highlightedSubtitleColor: #e4e5e8; + + qproperty-btnColor: #414A59; + qproperty-btnTextColor: white; + + qproperty-timestampColor: #727274; + qproperty-highlightedTimestampColor: #e7e7e9; + qproperty-hoverTimestampColor: #f4f5f8; + + qproperty-avatarBgColor: #202228; + qproperty-avatarFgColor: white; + + qproperty-bubbleFgColor: white; + qproperty-bubbleBgColor: #4d84c7; +} + CommunitiesListItem { qproperty-highlightedBackgroundColor: #4d84c7; qproperty-hoverBackgroundColor: rgba(230, 230, 230, 30); diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index f7dbf7ca..2f1a22b7 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -35,6 +35,7 @@ #include "TopRoomBar.h" #include "TypingDisplay.h" #include "UserInfoWidget.h" +#include "UserMentionsWidget.h" #include "UserSettingsPage.h" #include "Utils.h" #include "ui/OverlayModal.h" @@ -43,6 +44,7 @@ #include "notifications/Manager.h" #include "dialogs/ReadReceipts.h" +#include "dialogs/UserMentions.h" #include "timeline/TimelineViewManager.h" // TODO: Needs to be updated with an actual secret. @@ -89,10 +91,12 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(sidebarActions_, &SideBarActions::createRoom, this, &ChatPage::createRoom); user_info_widget_ = new UserInfoWidget(sideBar_); + user_mentions_widget_ = new UserMentionsWidget(sideBar_); room_list_ = new RoomList(sideBar_); connect(room_list_, &RoomList::joinRoom, this, &ChatPage::joinRoom); sideBarLayout_->addWidget(user_info_widget_); + sideBarLayout_->addWidget(user_mentions_widget_); sideBarLayout_->addWidget(room_list_); sideBarLayout_->addWidget(sidebarActions_); @@ -150,6 +154,25 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) trySync(); }); + connect(user_mentions_widget_, &UserMentionsWidget::clicked, this, [this]() { + http::client()->notifications( + 1000, + "", + "highlight", + [this](const mtx::responses::Notifications &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to retrieve notifications: {} ({})", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + emit highlightedNotifsRetrieved(std::move(res)); + }); + }); + connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL); connect(&connectivityTimer_, &QTimer::timeout, this, [=]() { if (http::client()->access_token().empty()) { @@ -497,6 +520,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom); connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendDesktopNotifications); + connect(this, &ChatPage::highlightedNotifsRetrieved, this, &ChatPage::showNotificationsDialog); connect(communitiesList_, &CommunitiesList::communityChanged, @@ -562,6 +586,8 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) if (hasNotifications && userSettings_->hasDesktopNotifications()) http::client()->notifications( 5, + "", + "", [this](const mtx::responses::Notifications &res, mtx::http::RequestErr err) { if (err) { @@ -960,6 +986,32 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) } } +void +ChatPage::showNotificationsDialog(const mtx::responses::Notifications &res) +{ + // TODO: This should NOT BE A DIALOG. Make the TimelineView support + // creating a timeline view from notifications (similarly to how it can show history views) + auto notifDialog = new dialogs::UserMentions(); + for (const auto &item : res.notifications) { + const auto event_id = QString::fromStdString(utils::event_id(item.event)); + + try { + const auto room_id = QString::fromStdString(item.room_id); + const auto user_id = utils::event_sender(item.event); + const auto body = utils::event_body(item.event); + + notifDialog->pushItem(event_id, user_id, body, room_id); + + } catch (const lmdb::error &e) { + nhlog::db()->warn("error while sending desktop notification: {}", e.what()); + } + } + notifDialog->setFixedWidth(width()); + notifDialog->setFixedHeight(height()); + notifDialog->raise(); + notifDialog->show(); +} + void ChatPage::tryInitialSync() { diff --git a/src/ChatPage.h b/src/ChatPage.h index 6e6f5aed..bb06e0eb 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -43,6 +43,7 @@ class TimelineViewManager; class TopRoomBar; class TypingDisplay; class UserInfoWidget; +class UserMentionsWidget; class UserSettings; class NotificationsManager; @@ -87,6 +88,8 @@ signals: void messageReply(const RelatedInfo &related); void notificationsRetrieved(const mtx::responses::Notifications &); + void highlightedNotifsRetrieved(const mtx::responses::Notifications &); + void uploadFailed(const QString &msg); void imageUploaded(const QString &roomid, @@ -204,6 +207,9 @@ private: //! Send desktop notification for the received messages. void sendDesktopNotifications(const mtx::responses::Notifications &); + void showNotificationsDialog(const mtx::responses::Notifications &); + + QStringList generateTypingUsers(const QString &room_id, const std::vector &typing_users); @@ -236,6 +242,8 @@ private: UserInfoWidget *user_info_widget_; + UserMentionsWidget *user_mentions_widget_; + // Keeps track of the users currently typing on each room. std::map> typingUsers_; QTimer *typingRefresher_; diff --git a/src/UserMentionsWidget.cpp b/src/UserMentionsWidget.cpp new file mode 100644 index 00000000..b7b24ad2 --- /dev/null +++ b/src/UserMentionsWidget.cpp @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include + +#include "MainWindow.h" +#include "UserMentionsWidget.h" +#include "Utils.h" +#include "ui/Ripple.h" +#include "ui/RippleOverlay.h" + +constexpr int MaxUnreadCountDisplayed = 99; + +struct WMetrics +{ + int maxHeight; + int iconSize; + int padding; + int unit; + + int unreadLineWidth; + int unreadLineOffset; + + int inviteBtnX; + int inviteBtnY; +}; + +WMetrics +getWMetrics(const QFont &font) +{ + WMetrics m; + + const int height = QFontMetrics(font).lineSpacing(); + + m.unit = height; + m.maxHeight = std::ceil((double)height * 3.8); + m.iconSize = std::ceil((double)height * 2.8); + m.padding = std::ceil((double)height / 2.0); + m.unreadLineWidth = m.padding - m.padding / 3; + m.unreadLineOffset = m.padding - m.padding / 4; + + m.inviteBtnX = m.iconSize + 2 * m.padding; + m.inviteBtnX = m.iconSize / 2.0 + m.padding + m.padding / 3.0; + + return m; +} + +UserMentionsWidget::UserMentionsWidget(QWidget *parent) + : QWidget(parent) + , isPressed_(false) + , unreadMsgCount_(0) +{ + init(parent); + + QFont f; + f.setPointSizeF(f.pointSizeF()); + + const int fontHeight = QFontMetrics(f).height(); + const int widgetMargin = fontHeight / 3; + const int contentHeight = fontHeight * 3; + + setFixedHeight(contentHeight + widgetMargin); + + topLayout_ = new QHBoxLayout(this); + topLayout_->setSpacing(0); + topLayout_->setMargin(widgetMargin); +} + +void +UserMentionsWidget::init(QWidget *parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + setMouseTracking(true); + setAttribute(Qt::WA_Hover); + + setFixedHeight(getWMetrics(QFont{}).maxHeight); + + QPainterPath path; + path.addRect(0, 0, parent->width(), height()); + + ripple_overlay_ = new RippleOverlay(this); + ripple_overlay_->setClipPath(path); + ripple_overlay_->setClipping(true); + + unreadCountFont_.setPointSizeF(unreadCountFont_.pointSizeF() * 0.8); + unreadCountFont_.setBold(true); + + bubbleDiameter_ = QFontMetrics(unreadCountFont_).averageCharWidth() * 3; +} + +// void +// UserMentionsWidget::resizeEvent(QResizeEvent *event) +// { +// Q_UNUSED(event); + +// const auto sz = utils::calculateSidebarSizes(QFont{}); + +// if (width() <= sz.small) { +// topLayout_->setContentsMargins(0, 0, logoutButtonSize_, 0); + +// } else { +// topLayout_->setMargin(5); +// } + +// QWidget::resizeEvent(event); +// } + +void +UserMentionsWidget::setPressedState(bool state) +{ + if (isPressed_ != state) { + isPressed_ = state; + update(); + } +} + +void +UserMentionsWidget::resizeEvent(QResizeEvent *) +{ + // Update ripple's clipping path. + QPainterPath path; + path.addRect(0, 0, width(), height()); + + const auto sidebarSizes = utils::calculateSidebarSizes(QFont{}); + + if (width() > sidebarSizes.small) + setToolTip(""); + else + setToolTip(""); + + ripple_overlay_->setClipPath(path); + ripple_overlay_->setClipping(true); +} + +void +UserMentionsWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() == Qt::RightButton) { + QWidget::mousePressEvent(event); + return; + } + + emit clicked(); + + setPressedState(true); + + // Ripple on mouse position by default. + QPoint pos = event->pos(); + qreal radiusEndValue = static_cast(width()) / 3; + + Ripple *ripple = new Ripple(pos); + + ripple->setRadiusEndValue(radiusEndValue); + ripple->setOpacityStartValue(0.15); + ripple->setColor(QColor("white")); + ripple->radiusAnimation()->setDuration(200); + ripple->opacityAnimation()->setDuration(400); + + ripple_overlay_->addRipple(ripple); +} + +void +UserMentionsWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter p(this); + p.setRenderHint(QPainter::TextAntialiasing); + p.setRenderHint(QPainter::SmoothPixmapTransform); + p.setRenderHint(QPainter::Antialiasing); + + auto wm = getWMetrics(QFont{}); + + QPen titlePen(titleColor_); + QPen subtitlePen(subtitleColor_); + + QFontMetrics metrics(QFont{}); + + if (isPressed_) { + p.fillRect(rect(), highlightedBackgroundColor_); + titlePen.setColor(highlightedTitleColor_); + subtitlePen.setColor(highlightedSubtitleColor_); + } else if (underMouse()) { + p.fillRect(rect(), hoverBackgroundColor_); + titlePen.setColor(hoverTitleColor_); + subtitlePen.setColor(hoverSubtitleColor_); + } else { + p.fillRect(rect(), backgroundColor_); + titlePen.setColor(titleColor_); + subtitlePen.setColor(subtitleColor_); + } + + // Description line with the default font. + int bottom_y = wm.maxHeight - wm.padding - metrics.ascent() / 2; + + const auto sidebarSizes = utils::calculateSidebarSizes(QFont{}); + + if (width() > sidebarSizes.small) { + QFont headingFont; + headingFont.setWeight(QFont::Medium); + p.setFont(headingFont); + p.setPen(titlePen); + + QFont tsFont; + tsFont.setPointSizeF(tsFont.pointSizeF() * 0.9); +#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) + const int msgStampWidth = QFontMetrics(tsFont).width("timestamp") + 4; +#else + const int msgStampWidth = QFontMetrics(tsFont).horizontalAdvance("timestamp") + 4; +#endif + // We use the full width of the widget if there is no unread msg bubble. + //const int bottomLineWidthLimit = (unreadMsgCount_ > 0) ? msgStampWidth : 0; + + // Name line. + QFontMetrics fontNameMetrics(headingFont); + int top_y = 2 * wm.padding + fontNameMetrics.ascent() / 2; + + const auto name = metrics.elidedText( + "Mentions", + Qt::ElideRight, + (width() - wm.iconSize - 2 * wm.padding - msgStampWidth) * 0.8); + p.drawText(QPoint(2 * wm.padding + wm.iconSize, top_y), name); + + p.setFont(QFont{}); + p.setPen(subtitlePen); + + // The limit is the space between the end of the avatar and the start of the + // timestamp. + int usernameLimit = + std::max(0, width() - 3 * wm.padding - msgStampWidth - wm.iconSize - 20); + auto userName = metrics.elidedText("Show Mentioned Messages", Qt::ElideRight, usernameLimit); + + p.setFont(QFont{}); + p.drawText(QPoint(2 * wm.padding + wm.iconSize, bottom_y), userName); + + // We show the last message timestamp. + p.save(); + if (isPressed_) { + p.setPen(QPen(highlightedTimestampColor_)); + } else if (underMouse()) { + p.setPen(QPen(hoverTimestampColor_)); + } else { + p.setPen(QPen(timestampColor_)); + } + + // p.setFont(tsFont); + // p.drawText(QPoint(width() - wm.padding - msgStampWidth, top_y), "timestamp"); + p.restore(); + } + + p.setPen(Qt::NoPen); + + if (unreadMsgCount_ > 0) { + QBrush brush; + brush.setStyle(Qt::SolidPattern); + + brush.setColor(mentionedColor()); + + if (isPressed_) + brush.setColor(bubbleFgColor()); + + p.setBrush(brush); + p.setPen(Qt::NoPen); + p.setFont(unreadCountFont_); + + // Extra space on the x-axis to accomodate the extra character space + // inside the bubble. + const int x_width = unreadMsgCount_ > MaxUnreadCountDisplayed + ? QFontMetrics(p.font()).averageCharWidth() + : 0; + + QRectF r(width() - bubbleDiameter_ - wm.padding - x_width, + bottom_y - bubbleDiameter_ / 2 - 5, + bubbleDiameter_ + x_width, + bubbleDiameter_); + + if (width() == sidebarSizes.small) + r = QRectF(width() - bubbleDiameter_ - 5, + height() - bubbleDiameter_ - 5, + bubbleDiameter_ + x_width, + bubbleDiameter_); + + p.setPen(Qt::NoPen); + p.drawEllipse(r); + + p.setPen(QPen(bubbleFgColor())); + + if (isPressed_) + p.setPen(QPen(bubbleBgColor())); + + auto countTxt = unreadMsgCount_ > MaxUnreadCountDisplayed + ? QString("99+") + : QString::number(unreadMsgCount_); + + p.setBrush(Qt::NoBrush); + p.drawText(r.translated(0, -0.5), Qt::AlignCenter, countTxt); + } + + if (!isPressed_ && hasUnreadMessages_) { + QPen pen; + pen.setWidth(wm.unreadLineWidth); + pen.setColor(highlightedBackgroundColor_); + + p.setPen(pen); + p.drawLine(0, wm.unreadLineOffset, 0, height() - wm.unreadLineOffset); + } +} \ No newline at end of file diff --git a/src/UserMentionsWidget.h b/src/UserMentionsWidget.h new file mode 100644 index 00000000..179f0026 --- /dev/null +++ b/src/UserMentionsWidget.h @@ -0,0 +1,164 @@ +#pragma once + +#include +#include +#include +#include +#include + +class FlatButton; +class RippleOverlay; + +class UserMentionsWidget : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor) + + Q_PROPERTY(QColor highlightedBackgroundColor READ highlightedBackgroundColor WRITE + setHighlightedBackgroundColor) + Q_PROPERTY( + QColor hoverBackgroundColor READ hoverBackgroundColor WRITE setHoverBackgroundColor) + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + + Q_PROPERTY(QColor avatarBgColor READ avatarBgColor WRITE setAvatarBgColor) + Q_PROPERTY(QColor avatarFgColor READ avatarFgColor WRITE setAvatarFgColor) + + Q_PROPERTY(QColor bubbleBgColor READ bubbleBgColor WRITE setBubbleBgColor) + Q_PROPERTY(QColor bubbleFgColor READ bubbleFgColor WRITE setBubbleFgColor) + + Q_PROPERTY(QColor titleColor READ titleColor WRITE setTitleColor) + Q_PROPERTY(QColor subtitleColor READ subtitleColor WRITE setSubtitleColor) + + Q_PROPERTY(QColor timestampColor READ timestampColor WRITE setTimestampColor) + Q_PROPERTY(QColor highlightedTimestampColor READ highlightedTimestampColor WRITE + setHighlightedTimestampColor) + Q_PROPERTY(QColor hoverTimestampColor READ hoverTimestampColor WRITE setHoverTimestampColor) + + Q_PROPERTY( + QColor highlightedTitleColor READ highlightedTitleColor WRITE setHighlightedTitleColor) + Q_PROPERTY(QColor highlightedSubtitleColor READ highlightedSubtitleColor WRITE + setHighlightedSubtitleColor) + + Q_PROPERTY(QColor hoverTitleColor READ hoverTitleColor WRITE setHoverTitleColor) + Q_PROPERTY(QColor hoverSubtitleColor READ hoverSubtitleColor WRITE setHoverSubtitleColor) + + Q_PROPERTY(QColor mentionedColor READ mentionedColor WRITE setMentionedColor) + Q_PROPERTY(QColor btnColor READ btnColor WRITE setBtnColor) + Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor) + +public: + UserMentionsWidget(QWidget *parent = 0); + + void updateUnreadMessageCount(int count); + void clearUnreadMessageCount() { updateUnreadMessageCount(0); }; + bool isPressed() const { return isPressed_; } + int unreadMessageCount() const { return unreadMsgCount_; } + QColor borderColor() const { return borderColor_; } + void setBorderColor(QColor &color) { borderColor_ = color; } + QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; } + QColor hoverBackgroundColor() const { return hoverBackgroundColor_; } + QColor hoverTitleColor() const { return hoverTitleColor_; } + QColor hoverSubtitleColor() const { return hoverSubtitleColor_; } + QColor hoverTimestampColor() const { return hoverTimestampColor_; } + QColor backgroundColor() const { return backgroundColor_; } + QColor avatarBgColor() const { return avatarBgColor_; } + QColor avatarFgColor() const { return avatarFgColor_; } + + QColor highlightedTitleColor() const { return highlightedTitleColor_; } + QColor highlightedSubtitleColor() const { return highlightedSubtitleColor_; } + QColor highlightedTimestampColor() const { return highlightedTimestampColor_; } + + QColor titleColor() const { return titleColor_; } + QColor subtitleColor() const { return subtitleColor_; } + QColor timestampColor() const { return timestampColor_; } + QColor btnColor() const { return btnColor_; } + QColor btnTextColor() const { return btnTextColor_; } + + QColor bubbleFgColor() const { return bubbleFgColor_; } + QColor bubbleBgColor() const { return bubbleBgColor_; } + QColor mentionedColor() const { return mentionedFontColor_; } + + void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; } + void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; } + void setHoverSubtitleColor(QColor &color) { hoverSubtitleColor_ = color; } + void setHoverTitleColor(QColor &color) { hoverTitleColor_ = color; } + void setHoverTimestampColor(QColor &color) { hoverTimestampColor_ = color; } + void setBackgroundColor(QColor &color) { backgroundColor_ = color; } + void setTimestampColor(QColor &color) { timestampColor_ = color; } + void setAvatarFgColor(QColor &color) { avatarFgColor_ = color; } + void setAvatarBgColor(QColor &color) { avatarBgColor_ = color; } + + void setHighlightedTitleColor(QColor &color) { highlightedTitleColor_ = color; } + void setHighlightedSubtitleColor(QColor &color) { highlightedSubtitleColor_ = color; } + void setHighlightedTimestampColor(QColor &color) { highlightedTimestampColor_ = color; } + + void setTitleColor(QColor &color) { titleColor_ = color; } + void setSubtitleColor(QColor &color) { subtitleColor_ = color; } + + void setBtnColor(QColor &color) { btnColor_ = color; } + void setBtnTextColor(QColor &color) { btnTextColor_ = color; } + + void setBubbleFgColor(QColor &color) { bubbleFgColor_ = color; } + void setBubbleBgColor(QColor &color) { bubbleBgColor_ = color; } + void setMentionedColor(QColor &color) { mentionedFontColor_ = color; } + +signals: + void clicked(); + +public slots: + void setPressedState(bool state); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + void init(QWidget *parent); + + RippleOverlay *ripple_overlay_; + + bool isPressed_ = false; + + bool hasUnreadMessages_ = true; + + int unreadMsgCount_ = 0; + + QHBoxLayout *topLayout_; + + QColor borderColor_; + QColor highlightedBackgroundColor_; + QColor hoverBackgroundColor_; + QColor backgroundColor_; + + QColor highlightedTitleColor_; + QColor highlightedSubtitleColor_; + + QColor titleColor_; + QColor subtitleColor_; + + QColor hoverTitleColor_; + QColor hoverSubtitleColor_; + + QColor btnColor_; + QColor btnTextColor_; + + QRectF acceptBtnRegion_; + QRectF declineBtnRegion_; + + // Fonts + QColor mentionedFontColor_; + QFont unreadCountFont_; + int bubbleDiameter_; + + QColor timestampColor_; + QColor highlightedTimestampColor_; + QColor hoverTimestampColor_; + + QColor avatarBgColor_; + QColor avatarFgColor_; + + QColor bubbleBgColor_; + QColor bubbleFgColor_; +}; \ No newline at end of file diff --git a/src/dialogs/UserMentions.cpp b/src/dialogs/UserMentions.cpp new file mode 100644 index 00000000..1a6c17e5 --- /dev/null +++ b/src/dialogs/UserMentions.cpp @@ -0,0 +1,59 @@ +#include + +#include "UserMentions.h" +#include "timeline/TimelineItem.h" + +using namespace dialogs; + +UserMentions::UserMentions(QWidget *parent) + : QWidget{parent} +{ + top_layout_ = new QVBoxLayout(this); + top_layout_->setSpacing(0); + top_layout_->setMargin(0); + + scroll_area_ = new QScrollArea(this); + scroll_area_->setWidgetResizable(true); + scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + scroll_widget_ = new QWidget(this); + scroll_widget_->setObjectName("scroll_widget"); + + // Height of the typing display. + QFont f; + f.setPointSizeF(f.pointSizeF() * 0.9); + const int bottomMargin = QFontMetrics(f).height() + 6; + + scroll_layout_ = new QVBoxLayout(scroll_widget_); + scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); + scroll_layout_->setSpacing(0); + scroll_layout_->setObjectName("timelinescrollarea"); + + scroll_area_->setWidget(scroll_widget_); + scroll_area_->setAlignment(Qt::AlignBottom); + + top_layout_->addWidget(scroll_area_); + + setLayout(top_layout_); +} + +void +UserMentions::pushItem(const QString &event_id, const QString &user_id, const QString &body, const QString &room_id) { + TimelineItem *view_item = + new TimelineItem(mtx::events::MessageType::Text, + user_id, + body, + true, + room_id, + scroll_widget_); + view_item->setEventId(event_id); + setUpdatesEnabled(false); + view_item->hide(); + + scroll_layout_->addWidget(view_item); + QTimer::singleShot(0, this, [view_item, this]() { + view_item->show(); + view_item->adjustSize(); + setUpdatesEnabled(true); + }); +} \ No newline at end of file diff --git a/src/dialogs/UserMentions.h b/src/dialogs/UserMentions.h new file mode 100644 index 00000000..ff68d8c8 --- /dev/null +++ b/src/dialogs/UserMentions.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include + +namespace dialogs { + +class UserMentions : public QWidget +{ + Q_OBJECT +public: + UserMentions(QWidget *parent = nullptr); + void pushItem(const QString &event_id, const QString &user_id, const QString &body, const QString &room_id); +private: + QVBoxLayout *top_layout_; + QVBoxLayout *scroll_layout_; + + QScrollArea *scroll_area_; + QWidget *scroll_widget_; + + +}; + +} \ No newline at end of file