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