Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfonticonengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#ifndef QT_NO_ICON
8
9#include <QtCore/qdebug.h>
10#include <QtCore/qfile.h>
11#include <QtCore/qset.h>
12
13#include <QtGui/qfontdatabase.h>
14#include <QtGui/qpainter.h>
15#include <QtGui/qpainterpath.h>
16#include <QtGui/qpalette.h>
17#include <QtGui/qtextlayout.h>
18
19#include <QtGui/private/qfont_p.h>
20#include <QtGui/private/qfontengine_p.h>
21
23
24using namespace Qt::StringLiterals;
25
26QFontIconEngine::QFontIconEngine(const QString &iconName, const QFont &font)
27 : m_iconName(iconName)
28 , m_iconFont(font)
29{
30 Q_ASSERT_X(font.styleStrategy() & QFont::NoFontMerging, "QFontIconEngine",
31 "Icon fonts must not use font merging");
32}
33
34QFontIconEngine::~QFontIconEngine() = default;
35
36QIconEngine *QFontIconEngine::clone() const
37{
38 return new QFontIconEngine(m_iconName, m_iconFont);
39}
40
41QString QFontIconEngine::key() const
42{
43 return u"QFontIconEngine("_s + m_iconFont.key() + u')';
44}
45
46QString QFontIconEngine::iconName()
47{
48 return m_iconName;
49}
50
51bool QFontIconEngine::isNull()
52{
53 const QString text = string();
54 if (!text.isEmpty()) {
55 const QChar c0 = text.at(0);
56 const QFontMetrics fontMetrics(m_iconFont);
57 if (c0.isHighSurrogate() && text.size() > 1)
58 return !fontMetrics.inFontUcs4(QChar::surrogateToUcs4(c0, text.at(1)));
59 return !fontMetrics.inFont(c0);
60 }
61
62 return glyph() == 0;
63}
64
65QList<QSize> QFontIconEngine::availableSizes(QIcon::Mode, QIcon::State)
66{
67 return {{16, 16}, {24, 24}, {48, 48}, {128, 128}};
68}
69
70QSize QFontIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
71{
72 if (isNull())
73 return QIconEngine::actualSize(size, mode, state);
74
75 QFont renderFont(m_iconFont);
76 renderFont.setPixelSize(size.height());
77 QSizeF result;
78 if (const glyph_t glyphIndex = glyph()) {
79 QFontEngine *engine = QFontPrivate::get(renderFont)->engineForScript(QChar::Script_Common);
80
81 const glyph_metrics_t gm = engine->boundingBox(glyphIndex);
82 const qreal glyph_x = gm.x.toReal();
83 const qreal glyph_y = gm.y.toReal();
84 const qreal glyph_width = (gm.x + gm.width).toReal() - glyph_x;
85 const qreal glyph_height = (gm.y + gm.height).toReal() - glyph_y;
86
87 if (glyph_width > .0 && glyph_height > .0)
88 result = {glyph_width, glyph_height};
89 } else if (const QString text = string(); !text.isEmpty()) {
90 const QFontMetricsF fm(renderFont);
91 result = {fm.horizontalAdvance(text), fm.tightBoundingRect(text).height()};
92 }
93 if (!result.isValid())
94 return QIconEngine::actualSize(size, mode, state);
95
96 return result.scaled(size, Qt::KeepAspectRatio).toSize();
97}
98
99QPixmap QFontIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
100{
101 return scaledPixmap(size, mode, state, 1.0);
102}
103
104QPixmap QFontIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
105{
106 const quint64 cacheKey = calculateCacheKey(mode, state);
107 const QSize fittingSize = actualSize(size, mode, state);
108 if (cacheKey != m_pixmapCacheKey || m_pixmap.deviceIndependentSize() != fittingSize
109 || m_pixmap.devicePixelRatio() != scale) {
110 m_pixmap = QPixmap(fittingSize * scale);
111 m_pixmap.fill(Qt::transparent);
112 m_pixmap.setDevicePixelRatio(scale);
113
114 if (!m_pixmap.isNull()) {
115 QPainter painter(&m_pixmap);
116 paint(&painter, QRect(QPoint(), fittingSize), mode, state);
117 }
118
119 m_pixmapCacheKey = cacheKey;
120 }
121
122 return m_pixmap;
123}
124
125void QFontIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
126{
127 Q_UNUSED(state);
128
129 painter->save();
130 QFont renderFont(m_iconFont);
131 renderFont.setPixelSize(rect.height());
132
133 QColor color = Qt::black;
134 QPalette palette;
135 switch (mode) {
136 case QIcon::Active:
137 color = palette.color(QPalette::Active, QPalette::Text);
138 break;
139 case QIcon::Normal:
140 color = palette.color(QPalette::Active, QPalette::Text);
141 break;
142 case QIcon::Disabled:
143 color = palette.color(QPalette::Disabled, QPalette::Text);
144 break;
145 case QIcon::Selected:
146 color = palette.color(QPalette::Active, QPalette::HighlightedText);
147 break;
148 }
149
150 if (glyph_t glyphIndex = glyph()) {
151 QFontEngine *engine = QFontPrivate::get(renderFont)->engineForScript(QChar::Script_Common);
152
153 const glyph_metrics_t gm = engine->boundingBox(glyphIndex);
154 const int glyph_x = qFloor(gm.x.toReal());
155 const int glyph_y = qFloor(gm.y.toReal());
156 const int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x;
157 const int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y;
158
159 if (glyph_width > 0 && glyph_height > 0) {
160 QFixedPoint pt(QFixed(-glyph_x), QFixed(-glyph_y));
161 QPainterPath path;
162 path.setFillRule(Qt::WindingFill);
163 engine->addGlyphsToPath(&glyphIndex, &pt, 1, &path, {});
164 // make the glyph fit tightly into rect
165 const QRectF pathBoundingRect = path.boundingRect();
166 // center the glyph inside the rect
167 const QPointF topLeft = rect.topLeft() - pathBoundingRect.topLeft()
168 + (QPointF(rect.width(), rect.height())
169 - QPointF(pathBoundingRect.width(), pathBoundingRect.height())) / 2;
170 painter->translate(topLeft);
171
172 painter->setRenderHint(QPainter::Antialiasing);
173 painter->setPen(Qt::NoPen);
174 painter->setBrush(color);
175 painter->drawPath(path);
176 }
177 } else if (const QString text = string(); !text.isEmpty()) {
178 painter->setFont(renderFont);
179 painter->setPen(color);
180 painter->drawText(rect, Qt::AlignCenter, text);
181 }
182 painter->restore();
183}
184
185QString QFontIconEngine::string() const
186{
187 return {};
188}
189
190glyph_t QFontIconEngine::glyph() const
191{
192 if (m_glyph == uninitializedGlyph) {
193 QFontEngine *engine = QFontPrivate::get(m_iconFont)->engineForScript(QChar::Script_Common);
194 if (engine)
195 m_glyph = engine->findGlyph(QLatin1StringView(m_iconName.toLatin1()));
196 if (!m_glyph) {
197 // May not be a named glyph, but there might be a ligature for the
198 // icon name.
199 QTextLayout layout(m_iconName, m_iconFont);
200 layout.beginLayout();
201 layout.createLine();
202 layout.endLayout();
203 const auto glyphRuns = layout.glyphRuns();
204 if (glyphRuns.size() == 1) {
205 const auto glyphIndexes = glyphRuns.first().glyphIndexes();
206 if (glyphIndexes.size() == 1)
207 m_glyph = glyphIndexes.first();
208 }
209 }
210 }
211 return m_glyph;
212}
213
214QT_END_NAMESPACE
215
216#endif // QT_NO_ICON
Combined button and popup list for selecting options.