File indexing completed on 2024-11-17 05:01:38
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2000, 2006 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org> 0004 SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0005 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 #include "kfontsettingsdata.h" 0011 #include <QApplication> 0012 #include <QCoreApplication> 0013 #include <QDBusConnection> 0014 #include <QDBusMessage> 0015 #include <QDBusReply> 0016 #include <QString> 0017 #include <QVariant> 0018 #include <qpa/qwindowsysteminterface.h> 0019 0020 #include <KSandbox> 0021 #include <kconfiggroup.h> 0022 0023 KFontSettingsData::KFontSettingsData() 0024 : QObject(nullptr) 0025 , mUsePortal(KSandbox::isInside()) 0026 , mKdeGlobals(KSharedConfig::openConfig()) 0027 { 0028 QMetaObject::invokeMethod(this, "delayedDBusConnects", Qt::QueuedConnection); 0029 0030 for (int i = 0; i < FontTypesCount; ++i) { 0031 mFonts[i] = nullptr; 0032 } 0033 } 0034 0035 KFontSettingsData::~KFontSettingsData() 0036 { 0037 for (int i = 0; i < FontTypesCount; ++i) { 0038 delete mFonts[i]; 0039 } 0040 } 0041 0042 // NOTE: keep in sync with plasma-desktop/kcms/fonts/fonts.cpp 0043 static const char GeneralId[] = "General"; 0044 static const char DefaultFont[] = "Noto Sans"; 0045 0046 static const KFontData DefaultFontData[KFontSettingsData::FontTypesCount] = { 0047 {GeneralId, "font", DefaultFont, 10, QFont::Normal, QFont::SansSerif, "Regular"}, 0048 {GeneralId, "fixed", "Hack", 10, QFont::Normal, QFont::Monospace, "Regular"}, 0049 {GeneralId, "toolBarFont", DefaultFont, 10, QFont::Normal, QFont::SansSerif, "Regular"}, 0050 {GeneralId, "menuFont", DefaultFont, 10, QFont::Normal, QFont::SansSerif, "Regular"}, 0051 {"WM", "activeFont", DefaultFont, 10, QFont::Normal, QFont::SansSerif, "Regular"}, 0052 {GeneralId, "taskbarFont", DefaultFont, 10, QFont::Normal, QFont::SansSerif, "Regular"}, 0053 {GeneralId, "smallestReadableFont", DefaultFont, 8, QFont::Normal, QFont::SansSerif, "Regular"}, 0054 }; 0055 0056 // From https://invent.kde.org/qt/qt/qtbase/blob/6.7/src/gui/text/qfont.cpp#L146 0057 static int convertWeights(int weight, bool inverted) 0058 { 0059 static constexpr int legacyToOpenTypeMap[][2] = { 0060 {0, 100}, 0061 {12, 200}, 0062 {25, 300}, 0063 {50, 400}, 0064 {57, 500}, 0065 {63, 600}, 0066 {75, 700}, 0067 {81, 800}, 0068 {87, 900}, 0069 }; 0070 0071 int closestDist = INT_MAX; 0072 int result = -1; 0073 0074 // Go through and find the closest mapped value 0075 for (const auto &mapping : legacyToOpenTypeMap) { 0076 const int weightOld = mapping[inverted]; 0077 const int weightNew = mapping[!inverted]; 0078 const int dist = qAbs(weightOld - weight); 0079 if (dist < closestDist) { 0080 result = weightNew; 0081 closestDist = dist; 0082 } else { 0083 // Break early since following values will be further away 0084 break; 0085 } 0086 } 0087 0088 return result; 0089 } 0090 0091 // Qt5: https://invent.kde.org/qt/qt/qtbase/blob/5.15/src/gui/text/qfont.cpp#L2110 0092 // Qt6: https://invent.kde.org/qt/qt/qtbase/blob/6.7/src/gui/text/qfont.cpp#L2135 0093 static QString convertQt6FontStringToQt5(const QString &fontInfo) 0094 { 0095 const auto parts = fontInfo.trimmed().split(QLatin1Char(',')); 0096 const int count = parts.count(); 0097 0098 if (count != 16 && count != 17) { 0099 return fontInfo; 0100 } 0101 0102 auto result = parts.mid(0, 10); 0103 result[4] = QString::number(convertWeights(parts[4].toInt(), true)); 0104 0105 if (count == 17) { 0106 result << parts.last(); 0107 } 0108 return result.join(QLatin1Char(',')); 0109 } 0110 0111 QFont *KFontSettingsData::font(FontTypes fontType) 0112 { 0113 QFont *cachedFont = mFonts[fontType]; 0114 0115 if (!cachedFont) { 0116 const KFontData &fontData = DefaultFontData[fontType]; 0117 cachedFont = new QFont(QLatin1String(fontData.FontName), fontData.Size, fontData.Weight); 0118 cachedFont->setStyleHint(fontData.StyleHint); 0119 0120 const QString fontInfo = readConfigValue(QLatin1String(fontData.ConfigGroupKey), QLatin1String(fontData.ConfigKey)); 0121 0122 // If we have serialized information for this font, restore it 0123 // NOTE: We are not using KConfig directly because we can't call QFont::QFont from here 0124 if (!fontInfo.isEmpty()) { 0125 cachedFont->fromString(convertQt6FontStringToQt5(fontInfo)); 0126 } 0127 // Don't set default font style names, as it prevents different font weights from being used (the QFont::Normal weight should work) 0128 0129 mFonts[fontType] = cachedFont; 0130 } 0131 0132 return cachedFont; 0133 } 0134 0135 void KFontSettingsData::dropFontSettingsCache() 0136 { 0137 mKdeGlobals->reparseConfiguration(); 0138 for (int i = 0; i < FontTypesCount; ++i) { 0139 delete mFonts[i]; 0140 mFonts[i] = nullptr; 0141 } 0142 0143 QWindowSystemInterface::handleThemeChange(nullptr); 0144 0145 if (qobject_cast<QApplication *>(QCoreApplication::instance())) { 0146 QApplication::setFont(*font(KFontSettingsData::GeneralFont)); 0147 } else { 0148 QGuiApplication::setFont(*font(KFontSettingsData::GeneralFont)); 0149 } 0150 } 0151 0152 void KFontSettingsData::delayedDBusConnects() 0153 { 0154 QDBusConnection::sessionBus().connect(QString(), 0155 QStringLiteral("/KDEPlatformTheme"), 0156 QStringLiteral("org.kde.KDEPlatformTheme"), 0157 QStringLiteral("refreshFonts"), 0158 this, 0159 SLOT(dropFontSettingsCache())); 0160 0161 if (mUsePortal) { 0162 QDBusConnection::sessionBus().connect(QString(), 0163 QStringLiteral("/org/freedesktop/portal/desktop"), 0164 QStringLiteral("org.freedesktop.portal.Settings"), 0165 QStringLiteral("SettingChanged"), 0166 this, 0167 SLOT(slotPortalSettingChanged(QString, QString, QDBusVariant))); 0168 } 0169 } 0170 0171 void KFontSettingsData::slotPortalSettingChanged(const QString &group, const QString &key, const QDBusVariant &value) 0172 { 0173 Q_UNUSED(value); 0174 0175 if (group == QLatin1String("org.kde.kdeglobals.General") && key == QLatin1String("font")) { 0176 dropFontSettingsCache(); 0177 } 0178 } 0179 0180 QString KFontSettingsData::readConfigValue(const QString &group, const QString &key, const QString &defaultValue) const 0181 { 0182 if (mUsePortal) { 0183 const QString settingName = QStringLiteral("org.kde.kdeglobals.%1").arg(group); 0184 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), 0185 QStringLiteral("/org/freedesktop/portal/desktop"), 0186 QStringLiteral("org.freedesktop.portal.Settings"), 0187 QStringLiteral("Read")); 0188 message << settingName << key; 0189 0190 // FIXME: async? 0191 QDBusReply<QVariant> reply = QDBusConnection::sessionBus().call(message); 0192 if (reply.isValid()) { 0193 QDBusVariant result = qvariant_cast<QDBusVariant>(reply.value()); 0194 const QString resultStr = result.variant().toString(); 0195 0196 if (!resultStr.isEmpty()) { 0197 return resultStr; 0198 } 0199 } 0200 } 0201 0202 const KConfigGroup configGroup(mKdeGlobals, group); 0203 return configGroup.readEntry(key, defaultValue); 0204 }