CoreText: Populate all variants of theme/system fonts
We populate the various QPlatformTheme::Fonts by asking the system for the preferred font for each use-case, but that just gives us a single font descriptor with a single style and weight. If the user then tweaks the font by e.g. making it bold or italic, our font database will not have any knowledge of these variants, and will fall back to another font. To fix this we ask CoreText for all variants of each of the theme fonts, so that each of the theme font families are fully populated. The preferred way to do this on macOS 10.15/iOS 13 and above is by using the font's UI design trait. This avoids asking CoreText for private fonts by family name directly, which is not supported and may result in giving us Times or another fallback font instead. Pick-to: 6.2 6.3 Change-Id: I4aa21624df62ac09a92d7ae0bc2cdde4f6ad6e5f Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
a068f2d7c9
commit
c7280b3362
@ -192,6 +192,7 @@ public:
|
||||
EditorFont,
|
||||
NFonts
|
||||
};
|
||||
Q_ENUM(Font)
|
||||
|
||||
enum StandardPixmap { // Keep in sync with QStyle::StandardPixmap
|
||||
TitleBarMenuButton,
|
||||
|
@ -110,9 +110,6 @@ QCoreTextFontDatabase::QCoreTextFontDatabase()
|
||||
QCoreTextFontDatabase::~QCoreTextFontDatabase()
|
||||
{
|
||||
qDeleteAll(m_themeFonts);
|
||||
|
||||
for (CTFontDescriptorRef ref : qAsConst(m_systemFontDescriptors))
|
||||
CFRelease(ref);
|
||||
}
|
||||
|
||||
void QCoreTextFontDatabase::populateFontDatabase()
|
||||
@ -130,8 +127,13 @@ void QCoreTextFontDatabase::populateFontDatabase()
|
||||
|
||||
populateThemeFonts();
|
||||
|
||||
for (CTFontDescriptorRef fontDesc : m_systemFontDescriptors)
|
||||
populateFromDescriptor(fontDesc);
|
||||
for (auto familyName : m_systemFontDescriptors.keys()) {
|
||||
for (auto fontDescriptor : m_systemFontDescriptors.value(familyName))
|
||||
populateFromDescriptor(fontDescriptor, familyName);
|
||||
}
|
||||
|
||||
// The font database now has a reference to the original descriptors
|
||||
m_systemFontDescriptors.clear();
|
||||
|
||||
qCDebug(lcQpaFonts) << "Populating system descriptors took" << elapsed.restart() << "ms";
|
||||
|
||||
@ -202,6 +204,8 @@ CTFontDescriptorRef descriptorForFamily(const char *familyName)
|
||||
|
||||
void QCoreTextFontDatabase::populateFamily(const QString &familyName)
|
||||
{
|
||||
qCDebug(lcQpaFonts) << "Populating family" << familyName;
|
||||
|
||||
// A single family might match several different fonts with different styles.
|
||||
// We need to add them all so that the font database has the full picture,
|
||||
// as once a family has been populated we will not populate it again.
|
||||
@ -749,6 +753,8 @@ static CTFontDescriptorRef fontDescriptorFromTheme(QPlatformTheme::Font f)
|
||||
|
||||
void QCoreTextFontDatabase::populateThemeFonts()
|
||||
{
|
||||
QMacAutoReleasePool pool;
|
||||
|
||||
if (!m_themeFonts.isEmpty())
|
||||
return;
|
||||
|
||||
@ -760,7 +766,7 @@ void QCoreTextFontDatabase::populateThemeFonts()
|
||||
|
||||
for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) {
|
||||
QPlatformTheme::Font themeFont = static_cast<QPlatformTheme::Font>(f);
|
||||
CTFontDescriptorRef fontDescriptor = fontDescriptorFromTheme(themeFont);
|
||||
QCFType<CTFontDescriptorRef> fontDescriptor = fontDescriptorFromTheme(themeFont);
|
||||
FontDescription fd;
|
||||
getFontDescription(fontDescriptor, &fd);
|
||||
|
||||
@ -770,10 +776,54 @@ void QCoreTextFontDatabase::populateThemeFonts()
|
||||
// would result in the font database having > 0 families, which would result in
|
||||
// skipping the initialization and population of all other font families. Instead
|
||||
// we store the descriptors for later and populate them during populateFontDatabase().
|
||||
if (!m_systemFontDescriptors.contains(fontDescriptor))
|
||||
m_systemFontDescriptors.insert(fontDescriptor);
|
||||
else
|
||||
CFRelease(fontDescriptor);
|
||||
|
||||
bool haveRegisteredFamily = m_systemFontDescriptors.contains(fd.familyName);
|
||||
qCDebug(lcQpaFonts) << "Got" << (haveRegisteredFamily ? "already registered" : "unseen")
|
||||
<< "family" << fd.familyName << "for" << themeFont;
|
||||
|
||||
if (!haveRegisteredFamily) {
|
||||
// We need to register all weights and variants of the theme font,
|
||||
// as the user might tweak the returned QFont before use.
|
||||
QList<QCFType<CTFontDescriptorRef>> themeFontVariants;
|
||||
|
||||
auto addFontVariants = [&](CTFontDescriptorRef descriptor) {
|
||||
QCFType<CFArrayRef> matchingDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr);
|
||||
const int matchingDescriptorsCount = CFArrayGetCount(matchingDescriptors);
|
||||
qCDebug(lcQpaFonts) << "Enumerating font variants based on" << id(descriptor)
|
||||
<< "resulted in" << matchingDescriptorsCount << "matching descriptors"
|
||||
<< matchingDescriptors.as<NSArray*>();
|
||||
|
||||
for (int i = 0; i < matchingDescriptorsCount; ++i) {
|
||||
auto matchingDescriptor = CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingDescriptors, i));
|
||||
themeFontVariants.append(QCFType<CTFontDescriptorRef>::constructFromGet(matchingDescriptor));
|
||||
}
|
||||
};
|
||||
|
||||
// Try populating the font variants based on its UI design trait, if available
|
||||
if (@available(macos 10.15, ios 13.0, *)) {
|
||||
auto fontTraits = QCFType<CFDictionaryRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontTraitsAttribute));
|
||||
static const NSString *kUIFontDesignTrait = @"NSCTFontUIFontDesignTrait";
|
||||
if (id uiFontDesignTrait = fontTraits.as<NSDictionary*>()[kUIFontDesignTrait]) {
|
||||
QCFType<CTFontDescriptorRef> designTraitDescriptor = CTFontDescriptorCreateWithAttributes(
|
||||
CFDictionaryRef(@{ (id)kCTFontTraitsAttribute: @{ kUIFontDesignTrait: uiFontDesignTrait }
|
||||
}));
|
||||
addFontVariants(designTraitDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (themeFontVariants.isEmpty()) {
|
||||
// Fall back to populating variants based on the family name alone
|
||||
QCFType<CTFontDescriptorRef> familyDescriptor = descriptorForFamily(fd.familyName);
|
||||
addFontVariants(familyDescriptor);
|
||||
}
|
||||
|
||||
if (themeFontVariants.isEmpty()) {
|
||||
qCDebug(lcQpaFonts) << "No theme font variants found, falling back to single variant descriptor";
|
||||
themeFontVariants.append(fontDescriptor);
|
||||
}
|
||||
|
||||
m_systemFontDescriptors.insert(fd.familyName, themeFontVariants);
|
||||
}
|
||||
|
||||
QFont *font = new QFont(fd.familyName, fd.pointSize, fd.weight, fd.style == QFont::StyleItalic);
|
||||
m_themeFonts.insert(themeFont, font);
|
||||
|
@ -86,9 +86,6 @@ public:
|
||||
// For iOS and macOS platform themes
|
||||
QFont *themeFont(QPlatformTheme::Font) const;
|
||||
|
||||
protected:
|
||||
mutable QSet<CTFontDescriptorRef> m_systemFontDescriptors;
|
||||
|
||||
private:
|
||||
void populateThemeFonts();
|
||||
void populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName = QString(), QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr);
|
||||
@ -97,6 +94,8 @@ private:
|
||||
mutable QString defaultFontName;
|
||||
|
||||
QHash<QPlatformTheme::Font, QFont *> m_themeFonts;
|
||||
QHash<QString, QList<QCFType<CTFontDescriptorRef>>> m_systemFontDescriptors;
|
||||
|
||||
bool m_hasPopulatedAliases;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user