From 2f122946392f5c6afb176034a7f84319d26216ab Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Wed, 18 Mar 2020 15:59:05 +0100 Subject: [PATCH] Fix CategoryListWidget icons - Correct colouring of selected icon - Better selection rectangle correction --- src/core/Resources.cpp | 24 ++++++++------- src/gui/CategoryListWidget.cpp | 55 ++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/core/Resources.cpp b/src/core/Resources.cpp index fab4eb168..90bc117ae 100644 --- a/src/core/Resources.cpp +++ b/src/core/Resources.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "config-keepassx.h" @@ -124,23 +125,24 @@ QIcon Resources::icon(const QString& name, bool recolor) icon = QIcon::fromTheme(name); if (getMainWindow() && recolor) { - QPixmap pixmap = icon.pixmap(128, 128); + QImage img = icon.pixmap(128, 128).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); icon = {}; QPalette palette = getMainWindow()->palette(); + QPainter painter(&img); + painter.setCompositionMode(QPainter::CompositionMode_SourceAtop); - auto mask = QBitmap::fromImage(pixmap.toImage().createAlphaMask()); - pixmap.fill(palette.color(QPalette::WindowText)); - pixmap.setMask(mask); - icon.addPixmap(pixmap, QIcon::Mode::Normal); + painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Normal, QPalette::WindowText)); + icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal); - pixmap.fill(palette.color(QPalette::HighlightedText)); - pixmap.setMask(mask); - icon.addPixmap(pixmap, QIcon::Mode::Selected); + painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::ButtonText)); + icon.addPixmap(QPixmap::fromImage(img), QIcon::Active); - pixmap.fill(palette.color(QPalette::Disabled, QPalette::WindowText)); - pixmap.setMask(mask); - icon.addPixmap(pixmap, QIcon::Mode::Disabled); + painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::HighlightedText)); + icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected); + + painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Disabled, QPalette::WindowText)); + icon.addPixmap(QPixmap::fromImage(img), QIcon::Disabled); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) icon.setIsMask(true); diff --git a/src/gui/CategoryListWidget.cpp b/src/gui/CategoryListWidget.cpp index 48ccb4783..d0c60dc52 100644 --- a/src/gui/CategoryListWidget.cpp +++ b/src/gui/CategoryListWidget.cpp @@ -168,22 +168,43 @@ public: const QWidget* widget) const override { painter->save(); - - if (PE_PanelItemViewItem == element) { + if (widget && PE_PanelItemViewItem == element) { // Qt on Windows and the Fusion/Phantom base styles draw selection backgrounds only for // the actual text/icon bounding box, not over the full width of a list item. - // We therefore need to translate and stretch the painter before we can - // tell Qt to draw its native styles. - // Since we are scaling horizontally, we also need to move the right and left - // edge pixels outside the drawing area to avoid thick border lines. - QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(1, 0, 1, 0); - painter->scale(static_cast(option->rect.width()) / itemRect.width(), 1.0); - painter->translate(option->rect.left() - itemRect.left() + 1, 0); + // State_On is relevant only for the Windows hack below. + if (option->state & State_HasFocus || option->state & State_On) { + painter->fillRect(option->rect, widget->palette().color(QPalette::Active, QPalette::Highlight)); + } else if (option->state & State_Selected) { + painter->fillRect(option->rect, widget->palette().color(QPalette::Inactive, QPalette::Highlight)); + } + } else if (PE_FrameFocusRect == element) { + // don't draw the native focus rect + } else { + QProxyStyle::drawPrimitive(element, option, painter, widget); } - QProxyStyle::drawPrimitive(element, option, painter, widget); painter->restore(); } + +#ifdef Q_OS_WIN + void drawControl(ControlElement element, + const QStyleOption* option, + QPainter* painter, + const QWidget* widget) const override + { + // Qt on Windows swallows State_HasFocus somewhere in its intestines, + // so we abuse State_On here to indicate the selection focus and + // hack into the text colour palette. Forgive me. + if (QStyle::CE_ItemViewItem == element && option->state & State_HasFocus) { + QStyleOptionViewItem newOpt(*qstyleoption_cast(option)); + newOpt.state |= State_On; + newOpt.palette.setColor(QPalette::All, QPalette::Text, widget->palette().color(QPalette::HighlightedText)); + QProxyStyle::drawControl(element, &newOpt, painter, widget); + return; + } + QProxyStyle::drawControl(element, option, painter, widget); + } +#endif }; void CategoryListWidgetDelegate::paint(QPainter* painter, @@ -209,7 +230,16 @@ void CategoryListWidgetDelegate::paint(QPainter* painter, int paddingTop = fontRect.height() < 30 ? 15 : 10; int left = opt.rect.left() + opt.rect.width() / 2 - iconSize.width() / 2; - painter->drawPixmap(left, opt.rect.top() + paddingTop, icon.pixmap(iconSize)); + + auto mode = QIcon::Normal; + if ((opt.state & QStyle::State_Enabled) == 0) { + mode = QIcon::Disabled; + } else if (opt.state & QStyle::State_HasFocus) { + mode = QIcon::Selected; + } else if (opt.state & QStyle::State_Active) { + mode = QIcon::Active; + } + painter->drawPixmap(left, opt.rect.top() + paddingTop, icon.pixmap(iconSize, mode)); painter->restore(); } @@ -231,7 +261,6 @@ int CategoryListWidgetDelegate::minWidth() const // add some padding maxWidth += 10; - return maxWidth < m_size.height() ? m_size.height() : maxWidth; } @@ -245,5 +274,5 @@ QSize CategoryListWidgetDelegate::sizeHint(const QStyleOptionViewItem& option, c w = m_listWidget->width(); } - return QSize(w, m_size.height()); + return {w, m_size.height()}; }