[network/ruqola] src/widgets: Fix layout
Laurent Montel <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 1865bdc6daadf94833f34030f408068fe250dfca by Laurent Montel.
Committed on 13/08/2026 at 16:50.
Pushed by mlaurent into branch 'master'.
Fix layout
M +6 -6 src/widgets/channellist/channellistdelegate.cpp
M +12 -6 src/widgets/common/emojicompletiondelegate.cpp
M +5 -5 src/widgets/misc/avatarcachemanager.cpp
M +7 -5 src/widgets/misc/joinedchannelcompletiondelegate.cpp
M +10 -5 src/widgets/room/commandpreviewimagedelegate.cpp
M +22 -7 src/widgets/room/delegate/messageattachmentdelegatehelpersound.cpp
M +2 -0 src/widgets/room/delegate/messageattachmentdelegatehelpersound.h
M +22 -7 src/widgets/room/delegate/messageattachmentdelegatehelpervideo.cpp
M +2 -0 src/widgets/room/delegate/messageattachmentdelegatehelpervideo.h
M +7 -6 src/widgets/room/messagetextedit.cpp
M +18 -7 src/widgets/switchchannelhistory/switchchanneldelegate.cpp
https://invent.kde.org/network/ruqola/-/commit/1865bdc6daadf94833f34030f408068fe250dfca
diff --git a/src/widgets/channellist/channellistdelegate.cpp b/src/widgets/channellist/channellistdelegate.cpp
index 30b4c6cb47..0d64b14eec 100644
--- a/src/widgets/channellist/channellistdelegate.cpp
+++ b/src/widgets/channellist/channellistdelegate.cpp
@@ -63,7 +63,7 @@ ChannelListDelegate::Layout ChannelListDelegate::doLayout(const QStyleOptionView
layout.unreadText = layout.isHeader ? QString() : makeUnreadText(index);
const int margin = DelegatePaintUtil::margin();
layout.unreadSize = !layout.unreadText.isEmpty() ? option.fontMetrics.size(Qt::TextSingleLine, layout.unreadText) : QSize(0, 0);
- layout.unreadRect = QRect(option.rect.width() - layout.unreadSize.width() - 2 * margin,
+ layout.unreadRect = QRect(option.rect.x() + option.rect.width() - layout.unreadSize.width() - 2 * margin,
option.rect.y() + padding,
layout.unreadSize.width() + margin,
layout.unreadSize.height());
@@ -114,10 +114,10 @@ void ChannelListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
const ChannelListDelegate::Layout layout = doLayout(option, index);
- const int xText = offsetAvatarRoom + option.rect.x() + iconSize + (isHeader ? 1 : 2) * margin;
- const QRect displayRect(xText,
+ const int xTextOffset = offsetAvatarRoom + iconSize + (isHeader ? 1 : 2) * margin;
+ const QRect displayRect(option.rect.x() + xTextOffset,
option.rect.y() + padding,
- option.rect.width() - xText - layout.unreadSize.width() - 2 * margin,
+ option.rect.width() - xTextOffset - layout.unreadSize.width() - 2 * margin,
option.rect.height() - extraMargins);
QStyleOptionViewItem optionCopy = option;
@@ -132,10 +132,10 @@ void ChannelListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
if (avatarInfo.isValid()) {
const QPixmap pix = mAvatarCacheManager->makeAvatarPixmap(option.widget, avatarInfo, option.rect.height() - extraMargins);
#if USE_ROUNDED_RECT_PIXMAP
- const QPointF pos(margin, option.rect.top() + padding);
+ const QPointF pos(option.rect.x() + margin, option.rect.top() + padding);
DelegatePaintUtil::createClipRoundedRectangle(painter, QRectF(pos, pix.deviceIndependentSize()), pix);
#else
- painter->drawPixmap(margin, option.rect.top() + padding, pix);
+ painter->drawPixmap(option.rect.x() + margin, option.rect.top() + padding, pix);
#endif
}
}
diff --git a/src/widgets/common/emojicompletiondelegate.cpp b/src/widgets/common/emojicompletiondelegate.cpp
index 2df1217958..4206870b07 100644
--- a/src/widgets/common/emojicompletiondelegate.cpp
+++ b/src/widgets/common/emojicompletiondelegate.cpp
@@ -30,18 +30,24 @@ void EmojiCompletionDelegate::paint(QPainter *painter, const QStyleOptionViewIte
const QIcon icon = index.data(EmoticonModel::IconRole).value<QIcon>();
const QString emojiText = index.data(EmoticonModel::UnicodeEmojiRole).toString();
+ const int xEmoji = option.rect.x() + margin;
if (!icon.isNull()) {
const int emojiWidth = emojiFontMetrics.horizontalAdvance(u"MM"_s);
- const QRect displayRect(margin, option.rect.y(), emojiWidth, option.rect.height());
- drawDecoration(painter, option, displayRect, icon.pixmap(emojiWidth, option.rect.height()));
- painter->drawText(margin + emojiWidth, option.rect.y() + emojiFontMetrics.ascent(), emojiText);
+ const QRect displayRect(xEmoji, option.rect.y(), emojiWidth, option.rect.height());
+ // Not drawDecoration(): it centers the pixmap using its device size, so on a HiDPI screen
+ // (where QIcon::pixmap() returns a devicePixelRatio > 1 pixmap) it lands outside displayRect.
+ const QPixmap pix = icon.pixmap(emojiWidth, option.rect.height());
+ const QSizeF pixSize = pix.deviceIndependentSize();
+ painter->drawPixmap(
+ QPointF(displayRect.x() + (displayRect.width() - pixSize.width()) / 2, displayRect.y() + (displayRect.height() - pixSize.height()) / 2),
+ pix);
+ painter->drawText(xEmoji + emojiWidth, option.rect.y() + emojiFontMetrics.ascent(), emojiText);
} else {
const int emojiWidth = emojiFontMetrics.horizontalAdvance(emojiText);
painter->setFont(mEmojiFont);
- painter->drawText(margin, option.rect.y() + emojiFontMetrics.ascent(), emojiText);
+ painter->drawText(xEmoji, option.rect.y() + emojiFontMetrics.ascent(), emojiText);
const QString text = index.data(EmoticonModel::IdentifierRole).toString();
- const int xText = option.rect.x() + margin + emojiWidth;
- const QRect displayRect(xText, option.rect.y(), option.rect.width() - xText, option.rect.height());
+ const QRect displayRect(xEmoji + emojiWidth, option.rect.y(), option.rect.width() - margin - emojiWidth, option.rect.height());
drawDisplay(painter, option, displayRect, text);
}
}
diff --git a/src/widgets/misc/avatarcachemanager.cpp b/src/widgets/misc/avatarcachemanager.cpp
index 9f3313533a..e9e6e49c32 100644
--- a/src/widgets/misc/avatarcachemanager.cpp
+++ b/src/widgets/misc/avatarcachemanager.cpp
@@ -175,16 +175,16 @@ QPixmap AvatarCacheManager::makeRoundedAvatarPixmap(const QWidget *widget, const
pix = pix.scaledToHeight(maxHeight * dpr, Qt::SmoothTransformation);
pix.setDevicePixelRatio(dpr);
- QPixmap fullScale(pix.size());
- fullScale.fill(Qt::transparent);
-
- downScaled = fullScale.scaledToHeight(maxHeight * dpr, Qt::SmoothTransformation);
+ downScaled = QPixmap(pix.size());
+ downScaled.fill(Qt::transparent);
downScaled.setDevicePixelRatio(dpr);
QPainterPath path;
QPainter p(&downScaled);
p.setRenderHint(QPainter::Antialiasing);
- path.addRoundedRect(downScaled.rect(), 5, 5);
+ // The painter paints in device-independent pixels, so the clip path must be expressed in
+ // them too: QPixmap::rect() is in device pixels and would be dpr times too large.
+ path.addRoundedRect(QRectF(QPointF(0, 0), downScaled.deviceIndependentSize()), 5, 5);
p.setClipPath(path);
p.drawPixmap(QPoint(0, 0), pix);
diff --git a/src/widgets/misc/joinedchannelcompletiondelegate.cpp b/src/widgets/misc/joinedchannelcompletiondelegate.cpp
index b63bb46c25..766940b926 100644
--- a/src/widgets/misc/joinedchannelcompletiondelegate.cpp
+++ b/src/widgets/misc/joinedchannelcompletiondelegate.cpp
@@ -45,12 +45,14 @@ void JoinedChannelCompletionDelegate::paint(QPainter *painter, const QStyleOptio
int xPos = -1;
const Utils::AvatarInfo info = index.data(JoinedChannelModel::AvatarInfo).value<Utils::AvatarInfo>();
if (info.isValid()) {
- const QRect displayRect(margin, option.rect.y(), option.rect.height(), option.rect.height());
- constexpr ushort marginTop = padding / 2;
- const QPixmap pix = mAvatarCacheManager->makeRoundedAvatarPixmap(option.widget, info, option.rect.height() - marginTop);
+ constexpr int marginTop = padding / 2;
+ const int iconSize = option.rect.height() - padding;
+ const QPixmap pix = mAvatarCacheManager->makeRoundedAvatarPixmap(option.widget, info, iconSize);
if (!pix.isNull()) {
- drawDecoration(painter, option, displayRect, pix);
- xPos = margin + option.rect.height();
+ // Not drawDecoration(): it aligns the pixmap using its device size, which offsets it
+ // when the devicePixelRatio is > 1.
+ painter->drawPixmap(option.rect.x() + margin, option.rect.y() + marginTop, iconSize, iconSize, pix);
+ xPos = margin + iconSize;
}
}
diff --git a/src/widgets/room/commandpreviewimagedelegate.cpp b/src/widgets/room/commandpreviewimagedelegate.cpp
index bfcff11f9e..2cf689fd3a 100644
--- a/src/widgets/room/commandpreviewimagedelegate.cpp
+++ b/src/widgets/room/commandpreviewimagedelegate.cpp
@@ -8,9 +8,14 @@
#include "model/previewcommandmodel.h"
#include <QListView>
#include <QPainter>
-CommandPreviewImageDelegate::CommandPreviewImageDelegate(QListView *view, QObject *parent)
+
+namespace
+{
+constexpr int margin = 5;
+}
+
+CommandPreviewImageDelegate::CommandPreviewImageDelegate(QObject *parent)
: QStyledItemDelegate{parent}
- , mView(view)
{
}
@@ -22,13 +27,13 @@ void CommandPreviewImageDelegate::paint(QPainter *painter, const QStyleOptionVie
QPixmap pixmap = index.data(static_cast<int>(PreviewCommandModel::PreviewCommandRoles::Image)).value<QPixmap>();
if (!pixmap.isNull()) {
- const QRect rect = option.rect.adjusted(5, 5, -5, -5);
+ const QRect rect = option.rect.adjusted(margin, margin, -margin, -margin);
painter->drawPixmap(rect, pixmap.scaled(rect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
if (hasFocus) {
painter->drawRect(option.rect.adjusted(0, 0, -1, -1));
}
} else {
- painter->drawText(option.rect, index.data(Qt::UserRole).toString());
+ painter->drawText(option.rect, index.data(Qt::DisplayRole).toString());
}
}
@@ -36,7 +41,7 @@ QSize CommandPreviewImageDelegate::sizeHint([[maybe_unused]] const QStyleOptionV
{
const QPixmap pixmap = index.data(static_cast<int>(PreviewCommandModel::PreviewCommandRoles::Image)).value<QPixmap>();
if (!pixmap.isNull()) {
- return pixmap.size();
+ return pixmap.size() + QSize(2 * margin, 2 * margin);
}
return QSize(60, 60);
}
diff --git a/src/widgets/room/delegate/messageattachmentdelegatehelpersound.cpp b/src/widgets/room/delegate/messageattachmentdelegatehelpersound.cpp
index f4d65345b8..42f7e2fbfd 100644
--- a/src/widgets/room/delegate/messageattachmentdelegatehelpersound.cpp
+++ b/src/widgets/room/delegate/messageattachmentdelegatehelpersound.cpp
@@ -40,9 +40,11 @@ void MessageAttachmentDelegateHelperSound::draw(const MessageAttachment &msgAtta
// Draw title and buttons
painter->drawText(messageRect.x(), messageRect.y() + option.fontMetrics.ascent(), layout.title);
mPlayerVolumeIcon.paint(painter, layout.playerVolumeButtonRect.translated(messageRect.topLeft()));
- mDownloadIcon.paint(painter, layout.downloadButtonRect.translated(messageRect.topLeft()));
+ if (layout.downloadButtonRect.isValid()) {
+ mDownloadIcon.paint(painter, layout.downloadButtonRect.translated(messageRect.topLeft()));
+ }
- const int nextY = messageRect.y() + layout.titleSize.height() + DelegatePaintUtil::margin();
+ const int nextY = messageRect.y() + layout.headerSize.height() + DelegatePaintUtil::margin();
drawDescription(msgAttach, messageRect, painter, nextY, index, option);
}
@@ -52,13 +54,13 @@ QSize MessageAttachmentDelegateHelperSound::sizeHint(const MessageAttachment &ms
const QStyleOptionViewItem &option) const
{
const SoundLayout layout = layoutSound(msgAttach, option, maxWidth);
- int height = layout.titleSize.height() + DelegatePaintUtil::margin();
+ int height = layout.headerSize.height() + DelegatePaintUtil::margin();
int descriptionWidth = 0;
if (layout.hasDescription) {
descriptionWidth = layout.descriptionSize.width();
height += layout.descriptionSize.height() + DelegatePaintUtil::margin();
}
- return {qMax(qMax(0, layout.titleSize.width()), descriptionWidth), height};
+ return {qMax(layout.headerSize.width(), descriptionWidth), height};
}
QPoint MessageAttachmentDelegateHelperSound::adaptMousePosition(const QPoint &pos,
@@ -67,7 +69,7 @@ QPoint MessageAttachmentDelegateHelperSound::adaptMousePosition(const QPoint &po
const QStyleOptionViewItem &option)
{
const SoundLayout layout = layoutSound(msgAttach, option, attachmentsRect.width());
- const QPoint relativePos = pos - attachmentsRect.topLeft() - QPoint(0, layout.titleSize.height() + DelegatePaintUtil::margin());
+ const QPoint relativePos = pos - attachmentsRect.topLeft() - QPoint(0, layout.headerSize.height() + DelegatePaintUtil::margin());
return relativePos;
}
@@ -80,8 +82,14 @@ bool MessageAttachmentDelegateHelperSound::handleMouseEvent(const MessageAttachm
const QEvent::Type eventType = mouseEvent->type();
switch (eventType) {
case QEvent::MouseButtonRelease: {
+ // Only a plain left click acts on the buttons: a release ending a text selection must go to the
+ // base class, otherwise selecting the description pops up the sound dialog.
+ if (mouseEvent->button() != Qt::LeftButton || mTextSelectionImpl->textSelection()->hasSelection()) {
+ break;
+ }
const QPoint pos = mouseEvent->pos();
+ // downloadButtonRect is invalid when the attachment can't be downloaded => it contains() nothing
const SoundLayout layout = layoutSound(msgAttach, option, attachmentsRect.width());
if (layout.downloadButtonRect.translated(attachmentsRect.topLeft()).contains(pos)) {
MessageAttachmentDownloadAndSaveJob::MessageAttachmentDownloadJobInfo info;
@@ -122,8 +130,15 @@ MessageAttachmentDelegateHelperSound::layoutSound(const MessageAttachment &msgAt
layout.titleSize = option.fontMetrics.size(Qt::TextSingleLine, layout.title);
layout.descriptionSize = documentTypeForIndexSize(convertAttachmentToDocumentDescriptionInfo(msgAttach, attachmentsWidth));
const int iconSize = option.widget->style()->pixelMetric(QStyle::PM_ButtonIconSize);
- layout.playerVolumeButtonRect = QRect(layout.titleSize.width() + DelegatePaintUtil::margin(), 0, iconSize, iconSize);
- layout.downloadButtonRect = layout.playerVolumeButtonRect.translated(iconSize + DelegatePaintUtil::margin(), 0);
+ const int headerHeight = qMax(layout.titleSize.height(), iconSize);
+ const int iconY = (headerHeight - iconSize) / 2;
+ layout.playerVolumeButtonRect = QRect(layout.titleSize.width() + DelegatePaintUtil::margin(), iconY, iconSize, iconSize);
+ int headerWidth = layout.playerVolumeButtonRect.right() + 1;
+ if (msgAttach.canDownloadAttachment()) {
+ layout.downloadButtonRect = layout.playerVolumeButtonRect.translated(iconSize + DelegatePaintUtil::margin(), 0);
+ headerWidth = layout.downloadButtonRect.right() + 1;
+ }
+ layout.headerSize = QSize(headerWidth, headerHeight);
layout.audioPath = msgAttach.link();
return layout;
}
diff --git a/src/widgets/room/delegate/messageattachmentdelegatehelpersound.h b/src/widgets/room/delegate/messageattachmentdelegatehelpersound.h
index 9b865f6bd0..2ca825ed6a 100644
--- a/src/widgets/room/delegate/messageattachmentdelegatehelpersound.h
+++ b/src/widgets/room/delegate/messageattachmentdelegatehelpersound.h
@@ -30,6 +30,8 @@ private:
QString audioPath;
QString title;
QSize titleSize;
+ // Title + buttons: what the first row really needs, the title alone is not enough
+ QSize headerSize;
QSize descriptionSize;
QRect playerVolumeButtonRect;
QRect downloadButtonRect;
diff --git a/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.cpp b/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.cpp
index e6e24e1c55..7eb4791c5a 100644
--- a/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.cpp
+++ b/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.cpp
@@ -41,9 +41,11 @@ void MessageAttachmentDelegateHelperVideo::draw(const MessageAttachment &msgAtta
painter->drawText(messageRect.x(), messageRect.y() + option.fontMetrics.ascent(), layout.title);
mVisibilityIcon.paint(painter, layout.showButtonRect.translated(messageRect.topLeft()));
- mDownloadIcon.paint(painter, layout.downloadButtonRect.translated(messageRect.topLeft()));
+ if (layout.downloadButtonRect.isValid()) {
+ mDownloadIcon.paint(painter, layout.downloadButtonRect.translated(messageRect.topLeft()));
+ }
- const int nextY = messageRect.y() + layout.titleSize.height() + DelegatePaintUtil::margin();
+ const int nextY = messageRect.y() + layout.headerSize.height() + DelegatePaintUtil::margin();
drawDescription(msgAttach, messageRect, painter, nextY, index, option);
}
@@ -54,13 +56,13 @@ QSize MessageAttachmentDelegateHelperVideo::sizeHint(const MessageAttachment &ms
const QStyleOptionViewItem &option) const
{
const VideoLayout layout = layoutVideo(msgAttach, option, maxWidth);
- int height = layout.titleSize.height() + DelegatePaintUtil::margin();
+ int height = layout.headerSize.height() + DelegatePaintUtil::margin();
int descriptionWidth = 0;
if (layout.hasDescription) {
descriptionWidth = layout.descriptionSize.width();
height += layout.descriptionSize.height() + DelegatePaintUtil::margin();
}
- return {qMax(qMax(0, layout.titleSize.width()), descriptionWidth), height};
+ return {qMax(layout.headerSize.width(), descriptionWidth), height};
}
bool MessageAttachmentDelegateHelperVideo::handleMouseEvent(const MessageAttachment &msgAttach,
@@ -72,8 +74,14 @@ bool MessageAttachmentDelegateHelperVideo::handleMouseEvent(const MessageAttachm
const QEvent::Type eventType = mouseEvent->type();
switch (eventType) {
case QEvent::MouseButtonRelease: {
+ // Only a plain left click acts on the buttons: a release ending a text selection must go to the
+ // base class, otherwise selecting the description pops up the video dialog.
+ if (mouseEvent->button() != Qt::LeftButton || mTextSelectionImpl->textSelection()->hasSelection()) {
+ break;
+ }
const QPoint pos = mouseEvent->pos();
+ // downloadButtonRect is invalid when the attachment can't be downloaded => it contains() nothing
const VideoLayout layout = layoutVideo(msgAttach, option, attachmentsRect.width());
if (layout.downloadButtonRect.translated(attachmentsRect.topLeft()).contains(pos)) {
MessageAttachmentDownloadAndSaveJob::MessageAttachmentDownloadJobInfo info;
@@ -114,8 +122,15 @@ MessageAttachmentDelegateHelperVideo::layoutVideo(const MessageAttachment &msgAt
layout.titleSize = option.fontMetrics.size(Qt::TextSingleLine, layout.title);
layout.descriptionSize = documentTypeForIndexSize(convertAttachmentToDocumentDescriptionInfo(msgAttach, attachmentsWidth));
const int iconSize = option.widget->style()->pixelMetric(QStyle::PM_ButtonIconSize);
- layout.showButtonRect = QRect(layout.titleSize.width() + DelegatePaintUtil::margin(), 0, iconSize, iconSize);
- layout.downloadButtonRect = layout.showButtonRect.translated(iconSize + DelegatePaintUtil::margin(), 0);
+ const int headerHeight = qMax(layout.titleSize.height(), iconSize);
+ const int iconY = (headerHeight - iconSize) / 2;
+ layout.showButtonRect = QRect(layout.titleSize.width() + DelegatePaintUtil::margin(), iconY, iconSize, iconSize);
+ int headerWidth = layout.showButtonRect.right() + 1;
+ if (msgAttach.canDownloadAttachment()) {
+ layout.downloadButtonRect = layout.showButtonRect.translated(iconSize + DelegatePaintUtil::margin(), 0);
+ headerWidth = layout.downloadButtonRect.right() + 1;
+ }
+ layout.headerSize = QSize(headerWidth, headerHeight);
layout.videoPath = msgAttach.link();
return layout;
}
@@ -126,6 +141,6 @@ QPoint MessageAttachmentDelegateHelperVideo::adaptMousePosition(const QPoint &po
const QStyleOptionViewItem &option)
{
const VideoLayout layout = layoutVideo(msgAttach, option, attachmentsRect.width());
- const QPoint relativePos = pos - attachmentsRect.topLeft() - QPoint(0, layout.titleSize.height() + DelegatePaintUtil::margin());
+ const QPoint relativePos = pos - attachmentsRect.topLeft() - QPoint(0, layout.headerSize.height() + DelegatePaintUtil::margin());
return relativePos;
}
diff --git a/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.h b/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.h
index f2c44d89a3..59d783f379 100644
--- a/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.h
+++ b/src/widgets/room/delegate/messageattachmentdelegatehelpervideo.h
@@ -31,6 +31,8 @@ private:
QString videoPath;
QString title;
QSize titleSize;
+ // Title + buttons: what the first row really needs, the title alone is not enough
+ QSize headerSize;
QSize descriptionSize;
QRect downloadButtonRect;
QRect showButtonRect;
diff --git a/src/widgets/room/messagetextedit.cpp b/src/widgets/room/messagetextedit.cpp
index 8717cbdf9f..d94e5fb2e6 100644
--- a/src/widgets/room/messagetextedit.cpp
+++ b/src/widgets/room/messagetextedit.cpp
@@ -134,8 +134,8 @@ void MessageTextEdit::switchAutoCorrectionLanguage(const QString &lang)
auto settings = Ruqola::self()->autoCorrection()->autoCorrectionSettings();
settings->setLanguage(lang);
Ruqola::self()->autoCorrection()->setAutoCorrectionSettings(settings);
+ qDebug() << " MessageTextEdit::switchAutoCorrectionLanguage " << lang;
}
- qDebug() << " MessageTextEdit::switchAutoCorrectionLanguage " << lang;
#endif
}
@@ -228,12 +228,13 @@ QMenu *MessageTextEdit::mousePopupMenu()
{
QMenu *menu = KTextEdit::mousePopupMenu();
- QClipboard *const clip = QApplication::clipboard();
- const QMimeData *mimeData = clip->mimeData();
- if (mimeData->hasImage()) {
+ // Don't store the QMimeData pointer: it's owned by the clipboard and is invalidated as soon as the clipboard contents change.
+ if (const QMimeData *mimeData = QApplication::clipboard()->mimeData(); mimeData && mimeData->hasImage()) {
menu->addSeparator();
- menu->addAction(i18n("Paste Image"), this, [this, mimeData]() {
- Q_EMIT handleMimeData(mimeData);
+ menu->addAction(i18n("Paste Image"), this, [this]() {
+ if (const QMimeData *currentMimeData = QApplication::clipboard()->mimeData(); currentMimeData && currentMimeData->hasImage()) {
+ Q_EMIT handleMimeData(currentMimeData);
+ }
});
}
menu->addSeparator();
diff --git a/src/widgets/switchchannelhistory/switchchanneldelegate.cpp b/src/widgets/switchchannelhistory/switchchanneldelegate.cpp
index 70bf15d192..b70b4e657c 100644
--- a/src/widgets/switchchannelhistory/switchchanneldelegate.cpp
+++ b/src/widgets/switchchannelhistory/switchchanneldelegate.cpp
@@ -15,7 +15,7 @@
namespace
{
-constexpr uint padding = 4;
+constexpr int padding = 4;
}
using namespace Qt::Literals::StringLiterals;
@@ -40,25 +40,36 @@ void SwitchChannelDelegate::paint(QPainter *painter, const QStyleOptionViewItem
drawBackground(painter, option, index);
const QString text = index.data(SwitchChannelHistoryModel::Name).toString();
const int margin = DelegatePaintUtil::margin();
+ // The row is padding pixels taller than the text, so inset the avatar by it instead of
+ // letting it fill the row edge to edge.
+ const int iconSize = option.rect.height() - padding;
int xPos = 0;
const Utils::AvatarInfo info = index.data(SwitchChannelHistoryModel::AvatarInfo).value<Utils::AvatarInfo>();
if (info.isValid()) {
- const QRect displayRect(option.rect.x() + margin, option.rect.y(), option.rect.height(), option.rect.height());
- const QPixmap pix = mAvatarCacheManager->makeRoundedAvatarPixmap(option.widget, info, option.rect.height());
+ const QPixmap pix = mAvatarCacheManager->makeRoundedAvatarPixmap(option.widget, info, iconSize);
if (!pix.isNull()) {
- drawDecoration(painter, option, displayRect, pix);
- xPos = margin + option.rect.height();
+ // Don't use drawDecoration() here: it aligns the pixmap using its device size, so an
+ // avatar with a devicePixelRatio > 1 ends up offset by half of it. Drawing into an
+ // explicit target rect is devicePixelRatio-correct.
+ painter->drawPixmap(option.rect.x() + margin, option.rect.y() + padding / 2, iconSize, iconSize, pix);
+ xPos = margin + iconSize;
}
}
const int xText = option.rect.x() + margin + xPos;
- const QRect displayRect(xText, option.rect.y(), option.rect.width() - xText, option.rect.height());
+ const QRect displayRect(xText, option.rect.y(), option.rect.width() - margin - xPos, option.rect.height());
drawDisplay(painter, option, displayRect, text);
}
QSize SwitchChannelDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
- return QItemDelegate::sizeHint(option, index) + QSize(0, padding);
+ QSize size = QItemDelegate::sizeHint(option, index) + QSize(0, padding);
+ const Utils::AvatarInfo info = index.data(SwitchChannelHistoryModel::AvatarInfo).value<Utils::AvatarInfo>();
+ if (info.isValid()) {
+ // Same layout as paint(): [M] <square avatar of (row height - padding)> [M] <name>
+ size.rwidth() += 2 * DelegatePaintUtil::margin() + size.height() - padding;
+ }
+ return size;
}
#include "moc_switchchanneldelegate.cpp"