Add initial implementation of a Windows icon engine
Implement an icon engine for Windows that renders glyphs from the Segoe Fluent Icons font on Windows 11 and the Segoe MDL2 Assets fonts on Windows 10. These fonts are installed on the respective Windows versions by default, and otherwise freely avialable for deployment. Icons from that font will mostly be based on single code points, but as the font is specifically designed to allow combining glyphs by layering, the implementation supports multiple code points as well, and can also use a surrogate pair as well to render e.g. an emoji. Task-number: QTBUG-102346 Change-Id: Ib47a267c3a1878d8f0e00dd954496fc338bb0110 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
5ae6355487
commit
d2e163d2e4
@ -17,6 +17,7 @@ qt_internal_add_plugin(QWindowsDirect2DIntegrationPlugin
|
||||
../windows/qwindowscursor.cpp ../windows/qwindowscursor.h
|
||||
../windows/qwindowsdialoghelpers.cpp ../windows/qwindowsdialoghelpers.h
|
||||
../windows/qwindowsdropdataobject.cpp ../windows/qwindowsdropdataobject.h
|
||||
../windows/qwindowsiconengine.cpp ../windows/qwindowsiconengine.h
|
||||
../windows/qwindowsinputcontext.cpp ../windows/qwindowsinputcontext.h
|
||||
../windows/qwindowsintegration.cpp ../windows/qwindowsintegration.h
|
||||
../windows/qwindowsinternalmimedata.cpp ../windows/qwindowsinternalmimedata.h
|
||||
|
@ -22,6 +22,7 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin
|
||||
qwindowsdropdataobject.cpp qwindowsdropdataobject.h
|
||||
qwindowsgdiintegration.cpp qwindowsgdiintegration.h
|
||||
qwindowsgdinativeinterface.cpp qwindowsgdinativeinterface.h
|
||||
qwindowsiconengine.cpp qwindowsiconengine.h
|
||||
qwindowsinputcontext.cpp qwindowsinputcontext.h
|
||||
qwindowsintegration.cpp qwindowsintegration.h
|
||||
qwindowsinternalmimedata.cpp qwindowsinternalmimedata.h
|
||||
|
147
src/plugins/platforms/windows/qwindowsiconengine.cpp
Normal file
147
src/plugins/platforms/windows/qwindowsiconengine.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include "qwindowsiconengine.h"
|
||||
|
||||
#include <QtCore/qoperatingsystemversion.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qpainter.h>
|
||||
#include <QtGui/qpalette.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QWindowsIconEngine::Glyphs QWindowsIconEngine::glyphs() const
|
||||
{
|
||||
if (!QFontInfo(m_iconFont).exactMatch())
|
||||
return {};
|
||||
|
||||
static constexpr std::pair<QStringView, Glyphs> glyphMap[] = {
|
||||
{u"edit-clear", 0xe894},
|
||||
{u"edit-copy", 0xe8c8},
|
||||
{u"edit-cut", 0xe8c6},
|
||||
{u"edit-delete", 0xe74d},
|
||||
{u"edit-find", 0xe721},
|
||||
{u"edit-find-replace", Glyphs(0xeb51, 0xeb52)},
|
||||
{u"edit-paste", 0xe77f},
|
||||
{u"edit-redo", 0xe7a6},
|
||||
{u"edit-select-all", 0xe8b3},
|
||||
{u"edit-undo", 0xe7a7},
|
||||
{u"printer", 0xe749},
|
||||
{u"red-heart", Glyphs(0x2764, 0xFE0F)},
|
||||
{u"banana", Glyphs(0xffff, 0xD83C, 0xDF4C)},
|
||||
};
|
||||
|
||||
const auto it = std::find_if(std::begin(glyphMap), std::end(glyphMap), [this](const auto &c){
|
||||
return c.first == m_iconName;
|
||||
});
|
||||
return it != std::end(glyphMap) ? it->second : Glyphs();
|
||||
}
|
||||
|
||||
namespace {
|
||||
auto iconFontFamily()
|
||||
{
|
||||
static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
|
||||
return isWindows11 ? u"Segoe Fluent Icons"_s
|
||||
: u"Segoe MDL2 Assets"_s;
|
||||
}
|
||||
}
|
||||
|
||||
QWindowsIconEngine::QWindowsIconEngine(const QString &iconName)
|
||||
: m_iconName(iconName), m_iconFont(iconFontFamily())
|
||||
, m_glyphs(glyphs())
|
||||
{
|
||||
}
|
||||
|
||||
QWindowsIconEngine::~QWindowsIconEngine()
|
||||
{}
|
||||
|
||||
QIconEngine *QWindowsIconEngine::clone() const
|
||||
{
|
||||
return new QWindowsIconEngine(m_iconName);
|
||||
}
|
||||
|
||||
QString QWindowsIconEngine::key() const
|
||||
{
|
||||
return u"QWindowsIconEngine"_s;
|
||||
}
|
||||
|
||||
QString QWindowsIconEngine::iconName()
|
||||
{
|
||||
return m_iconName;
|
||||
}
|
||||
|
||||
bool QWindowsIconEngine::isNull()
|
||||
{
|
||||
return m_glyphs.isNull();
|
||||
}
|
||||
|
||||
QList<QSize> QWindowsIconEngine::availableSizes(QIcon::Mode, QIcon::State)
|
||||
{
|
||||
return {{16, 16}, {24, 24}, {48, 48}, {128, 128}};
|
||||
}
|
||||
|
||||
QSize QWindowsIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
||||
{
|
||||
return QIconEngine::actualSize(size, mode, state);
|
||||
}
|
||||
|
||||
QPixmap QWindowsIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
||||
{
|
||||
return scaledPixmap(size, mode, state, 1.0);
|
||||
}
|
||||
|
||||
QPixmap QWindowsIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
|
||||
{
|
||||
const quint64 cacheKey = calculateCacheKey(mode, state);
|
||||
if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
|
||||
m_pixmap = QPixmap(size * scale);
|
||||
m_pixmap.fill(Qt::transparent);
|
||||
m_pixmap.setDevicePixelRatio(scale);
|
||||
|
||||
QPainter painter(&m_pixmap);
|
||||
QFont renderFont(m_iconFont);
|
||||
renderFont.setPixelSize(size.height());
|
||||
painter.setFont(renderFont);
|
||||
|
||||
QPalette palette;
|
||||
switch (mode) {
|
||||
case QIcon::Active:
|
||||
painter.setPen(palette.color(QPalette::Active, QPalette::Text));
|
||||
break;
|
||||
case QIcon::Normal:
|
||||
painter.setPen(palette.color(QPalette::Active, QPalette::Text));
|
||||
break;
|
||||
case QIcon::Disabled:
|
||||
painter.setPen(palette.color(QPalette::Disabled, QPalette::Text));
|
||||
break;
|
||||
case QIcon::Selected:
|
||||
painter.setPen(palette.color(QPalette::Active, QPalette::HighlightedText));
|
||||
break;
|
||||
}
|
||||
|
||||
const QRect rect({0, 0}, size);
|
||||
if (m_glyphs.codepoints[0] == QChar(0xffff)) {
|
||||
painter.drawText(rect, Qt::AlignCenter, QString(m_glyphs.codepoints + 1, 2));
|
||||
} else {
|
||||
for (const auto &glyph : m_glyphs.codepoints) {
|
||||
if (glyph.isNull())
|
||||
break;
|
||||
painter.drawText(rect, glyph);
|
||||
}
|
||||
}
|
||||
|
||||
m_cacheKey = cacheKey;
|
||||
}
|
||||
|
||||
return m_pixmap;
|
||||
}
|
||||
|
||||
void QWindowsIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
|
||||
{
|
||||
const qreal scale = painter->device()->devicePixelRatio();
|
||||
painter->drawPixmap(rect, scaledPixmap(rect.size(), mode, state, scale));
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
54
src/plugins/platforms/windows/qwindowsiconengine.h
Normal file
54
src/plugins/platforms/windows/qwindowsiconengine.h
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QWINDOWSICONENGINE_H
|
||||
#define QWINDOWSICONENGINE_H
|
||||
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
#include <QtGui/qfont.h>
|
||||
#include <QtGui/qiconengine.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWindowsIconEngine : public QIconEngine
|
||||
{
|
||||
public:
|
||||
QWindowsIconEngine(const QString &iconName);
|
||||
~QWindowsIconEngine();
|
||||
QIconEngine *clone() const override;
|
||||
QString key() const override;
|
||||
QString iconName() override;
|
||||
bool isNull() override;
|
||||
|
||||
QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override;
|
||||
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
|
||||
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
|
||||
QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
|
||||
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
|
||||
|
||||
private:
|
||||
static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state)
|
||||
{
|
||||
return (quint64(mode) << 32) | state;
|
||||
}
|
||||
struct Glyphs
|
||||
{
|
||||
constexpr Glyphs(char16_t g1 = 0, char16_t g2 = 0, char16_t g3 = 0) noexcept
|
||||
: codepoints{g1, g2, g3}
|
||||
{}
|
||||
constexpr bool isNull() const noexcept { return codepoints[0].isNull(); }
|
||||
const QChar codepoints[3] = {};
|
||||
};
|
||||
Glyphs glyphs() const;
|
||||
|
||||
const QString m_iconName;
|
||||
const QFont m_iconFont;
|
||||
const Glyphs m_glyphs;
|
||||
mutable QPixmap m_pixmap;
|
||||
mutable quint64 m_cacheKey = {};
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINDOWSICONENGINE_H
|
@ -7,6 +7,7 @@
|
||||
#include "qwindowsmenu.h"
|
||||
#include "qwindowsdialoghelpers.h"
|
||||
#include "qwindowscontext.h"
|
||||
#include "qwindowsiconengine.h"
|
||||
#include "qwindowsintegration.h"
|
||||
#if QT_CONFIG(systemtrayicon)
|
||||
# include "qwindowssystemtrayicon.h"
|
||||
@ -1174,6 +1175,14 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt
|
||||
return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions));
|
||||
}
|
||||
|
||||
QIconEngine *QWindowsTheme::createIconEngine(const QString &iconName) const
|
||||
{
|
||||
static bool experimentalIconEngines = qEnvironmentVariableIsSet("QT_ENABLE_EXPERIMENTAL_ICON_ENGINES");
|
||||
if (experimentalIconEngines)
|
||||
return new QWindowsIconEngine(iconName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool doUseNativeMenus()
|
||||
{
|
||||
const unsigned options = QWindowsIntegration::instance()->options();
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
|
||||
|
||||
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions = {}) const override;
|
||||
QIconEngine *createIconEngine(const QString &iconName) const override;
|
||||
|
||||
void windowsThemeChanged(QWindow *window);
|
||||
void displayChanged() { refreshIconPixmapSizes(); }
|
||||
|
Loading…
Reference in New Issue
Block a user