summary refs log tree commit diff
path: root/src/UserMentionsWidget.cpp
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2019-07-16 22:36:55 -0400
committerJoseph Donofry <joedonofry@gmail.com>2019-07-16 22:39:10 -0400
commit579bf234606a8974a457f244195d2b4f3594d294 (patch)
treef5289bf6d91731a0d4711ff5438c3e57d9f42d6d /src/UserMentionsWidget.cpp
parentUpdate Travis webhook URL for new nheko room (diff)
downloadnheko-579bf234606a8974a457f244195d2b4f3594d294.tar.xz
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.
Diffstat (limited to 'src/UserMentionsWidget.cpp')
-rw-r--r--src/UserMentionsWidget.cpp308
1 files changed, 308 insertions, 0 deletions
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 <QDateTime> +#include <QDebug> +#include <QMouseEvent> +#include <QPainter> +#include <QtGlobal> + +#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<qreal>(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