KGuiAddons

kiconutils.cpp
1/*
2 SPDX-FileCopyrightText: 2013 Martin Klapetek <mklapetek@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include <array>
8
9#include "kiconutils.h"
10
11#include <QHash>
12#include <QIconEngine>
13#include <QLibraryInfo>
14#include <QPainter>
15#include <QVersionNumber>
16
17class KOverlayIconEngine : public QIconEngine
18{
19public:
20 KOverlayIconEngine(const QIcon &icon, const QIcon &overlay, Qt::Corner position);
21 KOverlayIconEngine(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays);
22 KOverlayIconEngine(const QIcon &icon, const QStringList &overlays);
23 void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
24 QIconEngine *clone() const override;
25
26 QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
27 QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
28
29 void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override;
30 void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override;
31
32 void virtual_hook(int id, void *data) override;
33
34private:
35 QIcon m_base;
36 QHash<Qt::Corner, QIcon> m_overlays;
37 qreal m_dpr = 1.0;
38};
39
40KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QIcon &overlay, Qt::Corner position)
41 : QIconEngine()
42 , m_base(icon)
43{
44 m_overlays.insert(position, overlay);
45}
46
47KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays)
48 : QIconEngine()
49 , m_base(icon)
50 , m_overlays(overlays)
51{
52}
53
54KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QStringList &overlays)
55 : QIconEngine()
56 , m_base(icon)
57{
58 const std::array<Qt::Corner, 4> indexToCorner{
63 };
64
65 // static_cast becaue size() returns a qsizetype in Qt6
66 const int count = std::min(4, static_cast<int>(overlays.size()));
67
68 m_overlays.reserve(count);
69
70 for (int i = 0; i < count; i++) {
71 m_overlays.insert(indexToCorner[i], QIcon::fromTheme(overlays.at(i)));
72 }
73}
74
75QIconEngine *KOverlayIconEngine::clone() const
76{
77 return new KOverlayIconEngine(*this);
78}
79
80QSize KOverlayIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
81{
82 return m_base.actualSize(size, mode, state);
83}
84
85QPixmap KOverlayIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
86{
87 QPixmap pixmap(size);
88 pixmap.fill(Qt::transparent);
89 QPainter p(&pixmap);
90
91 paint(&p, pixmap.rect(), mode, state);
92
93 return pixmap;
94}
95
96void KOverlayIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
97{
98 m_base.addPixmap(pixmap, mode, state);
99}
100
101void KOverlayIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
102{
103 m_base.addFile(fileName, size, mode, state);
104}
105
106void KOverlayIconEngine::virtual_hook(int id, void *data)
107{
109 auto *info = reinterpret_cast<ScaledPixmapArgument *>(data);
110
111 QSize phyiscalSize = info->size;
112 // Since https://codereview.qt-project.org/c/qt/qtbase/+/563553 size is in logical pixels
113 if (QLibraryInfo::version() >= QVersionNumber(6, 8, 0)) {
114 phyiscalSize *= info->scale;
115 }
116
117 QPixmap pixmap(phyiscalSize);
118 pixmap.setDevicePixelRatio(info->scale);
119 pixmap.fill(Qt::transparent);
120
121 const QRect logicalRect(0, 0, phyiscalSize.width() / info->scale, phyiscalSize.height() / info->scale);
122 QPainter p(&pixmap);
123
124 m_dpr = info->scale;
125 paint(&p, logicalRect, info->mode, info->state);
126
127 info->pixmap = pixmap;
128
129 return;
130 }
132}
133
134void KOverlayIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
135{
136 // Get the pixel at the needed DPR
137 QPixmap pix = m_base.pixmap(rect.size(), m_dpr, mode, state);
138 pix.setDevicePixelRatio(m_dpr);
139
140 // draw the pix in the middle of the output pixmap
141 const auto pixMiddle = pix.size() / pix.devicePixelRatioF() / 2;
142 painter->drawPixmap(QPoint{rect.width() / 2 - pixMiddle.width(), rect.height() / 2 - pixMiddle.height()}, pix);
143
144 if (m_overlays.isEmpty()) {
145 return;
146 }
147
148 const int width = rect.width();
149 const int height = rect.height();
150 const int iconSize = qMin(width, height);
151 // Determine the overlay icon size
152 int overlaySize;
153 if (iconSize < 32) {
154 overlaySize = 8;
155 } else if (iconSize <= 48) {
156 overlaySize = 16;
157 } else if (iconSize <= 64) {
158 overlaySize = 22;
159 } else if (iconSize <= 96) {
160 overlaySize = 32;
161 } else if (iconSize <= 128) {
162 overlaySize = 48;
163 } else {
164 overlaySize = (int)(iconSize / 4);
165 }
166
167 // Iterate over stored overlays
168 QHash<Qt::Corner, QIcon>::const_iterator i = m_overlays.constBegin();
169 while (i != m_overlays.constEnd()) {
170 const QPixmap overlayPixmap = i.value().pixmap(QSize{overlaySize, overlaySize}, m_dpr, mode, state);
171 if (overlayPixmap.isNull()) {
172 ++i;
173 continue;
174 }
175
176 QPoint startPoint;
177 switch (i.key()) {
179 startPoint = QPoint(2, height - overlaySize - 2);
180 break;
182 startPoint = QPoint(width - overlaySize - 2, height - overlaySize - 2);
183 break;
185 startPoint = QPoint(width - overlaySize - 2, 2);
186 break;
188 startPoint = QPoint(2, 2);
189 break;
190 }
191
192 // Draw the overlay pixmap
193 painter->drawPixmap(startPoint, overlayPixmap);
194
195 ++i;
196 }
197}
198
199// ============================================================================
200
201namespace KIconUtils
202{
203QIcon addOverlay(const QIcon &icon, const QIcon &overlay, Qt::Corner position)
204{
205 return QIcon(new KOverlayIconEngine(icon, overlay, position));
206}
207
208QIcon addOverlays(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays)
209{
210 return QIcon(new KOverlayIconEngine(icon, overlays));
211}
212
213QIcon addOverlays(const QIcon &icon, const QStringList &overlays)
214{
215 if (overlays.count() == 0) {
216 return icon;
217 }
218
219 return QIcon(new KOverlayIconEngine(icon, overlays));
220}
221
222QIcon addOverlays(const QString &iconName, const QStringList &overlays)
223{
224 const QIcon icon = QIcon::fromTheme(iconName);
225
226 return addOverlays(icon, overlays);
227}
228}
Provides utility functions for icons.
QIcon addOverlays(const QIcon &icon, const QHash< Qt::Corner, QIcon > &overlays)
Adds overlays over the icon.
QIcon addOverlay(const QIcon &icon, const QIcon &overlay, Qt::Corner position)
Adds the overlay over the icon in the specified position.
QIcon fromTheme(const QString &name)
virtual void virtual_hook(int id, void *data)
QVersionNumber version()
const_reference at(qsizetype i) const const
qsizetype count() const const
qsizetype size() const const
qreal devicePixelRatioF() const const
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
bool isNull() const const
void setDevicePixelRatio(qreal scaleFactor)
QSize size() const const
int height() const const
QSize size() const const
int width() const const
int height() const const
void scale(const QSize &size, Qt::AspectRatioMode mode)
int width() const const
transparent
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:52:27 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.