mirror of https://github.com/Nheko-Reborn/nheko
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.pull/100/head
parent
377c9e4a79
commit
579bf23460
Binary file not shown.
@ -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); |
||||
} |
||||
} |
@ -0,0 +1,164 @@ |
||||
#pragma once |
||||
|
||||
#include <QColor> |
||||
#include <QHBoxLayout> |
||||
#include <QLabel> |
||||
#include <QLayout> |
||||
#include <QWidget> |
||||
|
||||
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_; |
||||
}; |
@ -0,0 +1,59 @@ |
||||
#include <QTimer> |
||||
|
||||
#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); |
||||
}); |
||||
} |
@ -0,0 +1,26 @@ |
||||
#pragma once |
||||
|
||||
#include <QWidget> |
||||
#include <QVBoxLayout> |
||||
#include <QScrollArea> |
||||
#include <QScrollBar> |
||||
|
||||
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_; |
||||
|
||||
|
||||
}; |
||||
|
||||
} |
Loading…
Reference in new issue