diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2023-03-23 12:45:46 +0100 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2023-05-09 23:39:41 +0200 |
commit | 6160ea45b689e9d26795a18f155053ac4dc4dd6b (patch) | |
tree | a183ff7330bb44227253b8719db90956eed98bcf | |
parent | b1d59d6dd97e99086d15b29765282866500942f3 (diff) | |
download | qtbase-6160ea45b689e9d26795a18f155053ac4dc4dd6b.tar.gz |
Implement API for enabling / disabling OpenType features
Similar to the font-features-settings in CSS, this is a low-level
API that allows you to pass the information to the shaper in order
to enable or disable specific font features by name.
[ChangeLog][QtGui][Text] Added an API to QFont which makes it
possible to enable and disable specific typographic features
in OpenType fonts.
Change-Id: Ib48c678f3b97a5a562b08ae34dc895800c8885c0
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
-rw-r--r-- | src/gui/text/qfont.cpp | 210 | ||||
-rw-r--r-- | src/gui/text/qfont.h | 13 | ||||
-rw-r--r-- | src/gui/text/qfont_p.h | 4 | ||||
-rw-r--r-- | src/gui/text/qtextengine.cpp | 47 | ||||
-rw-r--r-- | src/gui/text/qtextengine_p.h | 11 | ||||
-rw-r--r-- | tests/manual/fontfeatures/fontfeatures.pro | 17 | ||||
-rw-r--r-- | tests/manual/fontfeatures/main.cpp | 14 | ||||
-rw-r--r-- | tests/manual/fontfeatures/mainwindow.cpp | 225 | ||||
-rw-r--r-- | tests/manual/fontfeatures/mainwindow.h | 32 | ||||
-rw-r--r-- | tests/manual/fontfeatures/mainwindow.ui | 116 | ||||
-rw-r--r-- | tests/manual/manual.pro | 1 |
11 files changed, 673 insertions, 17 deletions
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 1efe3c06d8..94529badec 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -213,7 +213,7 @@ QFontPrivate::QFontPrivate(const QFontPrivate &other) strikeOut(other.strikeOut), kerning(other.kerning), capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute), letterSpacing(other.letterSpacing), wordSpacing(other.wordSpacing), - scFont(other.scFont) + fontFeatures(other.fontFeatures), scFont(other.scFont) { if (scFont && scFont != this) scFont->ref.ref(); @@ -343,9 +343,20 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other) wordSpacing = other->wordSpacing; if (! (mask & QFont::CapitalizationResolved)) capital = other->capital; + + if (!(mask & QFont::FontFeaturesResolved)) + fontFeatures = other->fontFeatures; } +void QFontPrivate::setFontFeature(quint32 tag, quint32 value) +{ + fontFeatures.insert(tag, value); +} +void QFontPrivate::unsetFontFeature(quint32 tag) +{ + fontFeatures.remove(tag); +} QFontEngineData::QFontEngineData() @@ -1748,6 +1759,7 @@ bool QFont::operator==(const QFont &f) const && f.d->letterSpacingIsAbsolute == d->letterSpacingIsAbsolute && f.d->letterSpacing == d->letterSpacing && f.d->wordSpacing == d->wordSpacing + && f.d->fontFeatures == d->fontFeatures )); } @@ -1785,7 +1797,21 @@ bool QFont::operator<(const QFont &f) const int f1attrs = (f.d->underline << 3) + (f.d->overline << 2) + (f.d->strikeOut<<1) + f.d->kerning; int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning; - return f1attrs < f2attrs; + if (f1attrs != f2attrs) return f1attrs < f2attrs; + + if (d->fontFeatures.size() != f.d->fontFeatures.size()) + return f.d->fontFeatures.size() < d->fontFeatures.size(); + + auto it = d->fontFeatures.constBegin(); + auto jt = f.d->fontFeatures.constBegin(); + for (; it != d->fontFeatures.constEnd(); ++it, ++jt) { + if (it.key() != jt.key()) + return jt.key() < it.key(); + if (it.value() != jt.value()) + return jt.value() < it.value(); + } + + return false; } @@ -2206,6 +2232,179 @@ void QFont::cacheStatistics() { } +/*! + \since 6.6 + + Applies integer values to specific OpenType features when shaping the text based on the contents + in \a fontFeatures. This provides advanced access to the font shaping process, and can be used + to support font features that are otherwise not covered in the API. + + An OpenType feature is defined by a 32-bit tag (encoded from the four-character name of the + table by using the stringToTag() function), as well as an integer value. + + This integer value passed along with the tag in most cases represents a boolean value: A zero + value means the feature is disabled, and a non-zero value means it is enabled. For certain + font features, however, it may have other intepretations. For example, when applied to the + \c salt feature, the value is an index that specifies the stylistic alternative to use. + + For example, the \c frac font feature will convert diagonal fractions separated with a slash + (such as \c 1/2) with a different representation. Typically this will involve baking the full + fraction into a single character width (such as \c ½). + + If a font supports the \c frac feature, then it can be enabled in the shaper by setting + \c{fontFeatures[stringToTag("frac")] = 1} in the font feature map. + + This function will overwrite the current list of explicit font features. Use setFontFeature() or + unsetFontFeature() to set or unset individual features. + + \note By default, Qt will enable and disable certain font features based on other font + properties. In particular, the \c kern feature will be enabled/disabled depending on the + \l kerning() property of the QFont. In addition, all ligature features + (\c liga, \c clig, \c dlig, \c hlig) will be disabled if a \l letterSpacing() is applied, + but only for writing systems where the use of ligature is cosmetic. For writing systems where + ligatures are required, the features will remain in their default state. The values set using + setFontFeatures() and related functions will override the default behavior. If, for instance, + the \c{fontFeatures[stringToTag("kern")]} is set to 1, then kerning will always be enabled, + regardless of whether the kerning property is set to false. Similarly, if it is set to 0, then + it will always be disabled. To reset a font feature to its default behavior, you can unset it + in the fontFeatures hash, for example by using unsetFontFeature(). + + \sa setFontFeature(), unsetFontFeature(), fontFeatures() +*/ +void QFont::setFontFeatures(const QHash<quint32, quint32> &fontFeatures) +{ + d->detachButKeepEngineData(this); + d->fontFeatures = fontFeatures; + resolve_mask |= QFont::FontFeaturesResolved; +} + +/*! + \since 6.6 + \overload + + Sets the \a value for a specific font feature \a tag. This is an advanced feature which can be + used to enable or disable specific OpenType features if they are available in the font. See + \l setFontFeatures() for more details. + + \sa setFontFeatures(), unsetFontFeature(), fontFeatures() +*/ +void QFont::setFontFeature(quint32 tag, quint32 value) +{ + d->detachButKeepEngineData(this); + d->setFontFeature(tag, value); + resolve_mask |= QFont::FontFeaturesResolved; +} + +/*! + \since 6.6 + \overload + + Sets the \a value of a specific \a fontFeature. This is an advanced feature which can be used to + enable or disable specific OpenType features if they are available in the font. See + \l setFontFeatures() for more details. + + \note This is equivalent to calling setFontFeature(stringToTag(fontFeature), value). + + \sa setFontFeatures(), unsetFontFeature(), fontFeatures() +*/ +void QFont::setFontFeature(const char *fontFeature, quint32 value) +{ + setFontFeature(stringToTag(fontFeature), value); +} + +/*! + \since 6.6 + \overload + + Unsets the \a fontFeature from the map of explicitly enabled/disabled features. + + \note Even if the feature has not previously been added, this will mark the font features map + as modified in this QFont, so that it will take precedence when resolving against other fonts. + + Unsetting an existing feature on the QFont reverts behavior to the default. See + \l setFontFeatures() for more details. + + \sa setFontFeatures(), setFontFeature(), fontFeatures() +*/ +void QFont::unsetFontFeature(quint32 tag) +{ + d->detachButKeepEngineData(this); + d->unsetFontFeature(tag); + resolve_mask |= QFont::FontFeaturesResolved; +} + +/*! + \since 6.6 + \overload + + Unsets the \a fontFeature from the map of explicitly enabled/disabled features. + + \note Even if the feature has not previously been added, this will mark the font features map + as modified in this QFont, so that it will take precedence when resolving against other fonts. + + Unsetting an existing feature on the QFont reverts behavior to the default. See + \l setFontFeatures() for more details. + + \note This is equivalent to calling unsetFontFeature(stringToTag(fontFeature)). + + \sa setFontFeatures(), setFontFeature(), fontFeatures() +*/ +void QFont::unsetFontFeature(const char *fontFeature) +{ + unsetFontFeature(stringToTag(fontFeature)); +} + +/*! + \since 6.6 + + Returns the hash of explicitly set font features in the QFont. By default this map is empty and + the shaping process will use default features based on other font or text properties. + + Unsetting an existing feature on the QFont reverts behavior to the default. See + \l setFontFeatures() for more details. + + The key of the returned QHash refers to the font table tag as it's encoded in the font + file. It can be converted to a QByteArray using the tagToString() function. + + \sa setFontFeatures(), setFontFeature(), unsetFontFeature() +*/ +QHash<quint32, quint32> QFont::fontFeatures() const +{ + return d->fontFeatures; +} + +/*! + \since 6.6 + + Returns the decoded name for \a tag. + + \sa setFontFeatures(), setFontFeature(), unsetFontFeature(), stringToTag() +*/ +QByteArray QFont::tagToString(quint32 tag) +{ + char str[4] = + { char((tag & 0xff000000) >> 24), + char((tag & 0x00ff0000) >> 16), + char((tag & 0x0000ff00) >> 8), + char((tag & 0x000000ff)) }; + return QByteArray(str, 4); +} + +/*! + \since 6.6 + + Returns the encoded tag for \a name. The \a name must be a null-terminated string of exactly + four characters. Returns 0 on error. + + \sa setFontFeatures(), setFontFeature(), unsetFontFeature(), tagToString() +*/ +quint32 QFont::stringToTag(const char *name) +{ + if (qstrlen(name) != 4) + return 0; + + return MAKE_TAG(name[0], name[1], name[2], name[3]); +} extern QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script); @@ -2343,6 +2542,8 @@ QDataStream &operator<<(QDataStream &s, const QFont &font) else s << font.d->request.families; } + if (s.version() >= QDataStream::Qt_6_6) + s << font.d->fontFeatures; return s; } @@ -2457,6 +2658,11 @@ QDataStream &operator>>(QDataStream &s, QFont &font) else font.d->request.families = value; } + if (s.version() >= QDataStream::Qt_6_6) { + font.d->fontFeatures.clear(); + s >> font.d->fontFeatures; + } + return s; } diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index 6a5c890395..6fa5b7b044 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -126,7 +126,8 @@ public: HintingPreferenceResolved = 0x8000, StyleNameResolved = 0x10000, FamiliesResolved = 0x20000, - AllPropertiesResolved = 0x3ffff + FontFeaturesResolved = 0x40000, + AllPropertiesResolved = 0x7ffff }; Q_ENUM(ResolveProperties) @@ -206,6 +207,16 @@ public: void setHintingPreference(HintingPreference hintingPreference); HintingPreference hintingPreference() const; + void setFontFeature(const char *fontFeature, quint32 value); + void setFontFeature(quint32 tag, quint32 value); + void setFontFeatures(const QHash<quint32, quint32> &fontFeatures); + void unsetFontFeature(quint32 tag); + void unsetFontFeature(const char *tag); + QHash<quint32, quint32> fontFeatures() const; + + static QByteArray tagToString(quint32 tag); + static quint32 stringToTag(const char *tagString); + // dupicated from QFontInfo bool exactMatch() const; diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 13738bb096..ef52410a0c 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -164,6 +164,7 @@ public: QFixed letterSpacing; QFixed wordSpacing; + QHash<quint32, quint32> fontFeatures; mutable QFontPrivate *scFont; QFont smallCapsFont() const { return QFont(smallCapsFontPrivate()); } @@ -178,6 +179,9 @@ public: static void detachButKeepEngineData(QFont *font); + void setFontFeature(quint32 tag, quint32 value); + void unsetFontFeature(quint32 tag); + private: QFontPrivate &operator=(const QFontPrivate &) { return *this; } }; diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index eded3e3f33..a8e70c17bf 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1404,6 +1404,7 @@ void QTextEngine::shapeText(int item) const bool kerningEnabled; bool letterSpacingIsAbsolute; bool shapingEnabled = false; + QHash<quint32, quint32> fontFeatures; QFixed letterSpacing, wordSpacing; #ifndef QT_NO_RAWFONT if (useRawFont) { @@ -1417,6 +1418,7 @@ void QTextEngine::shapeText(int item) const wordSpacing = QFixed::fromReal(font.wordSpacing()); letterSpacing = QFixed::fromReal(font.letterSpacing()); letterSpacingIsAbsolute = true; + fontFeatures = font.d->fontFeatures; } else #endif { @@ -1429,6 +1431,7 @@ void QTextEngine::shapeText(int item) const letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute; letterSpacing = font.d->letterSpacing; wordSpacing = font.d->wordSpacing; + fontFeatures = font.d->fontFeatures; if (letterSpacingIsAbsolute && letterSpacing.value()) letterSpacing *= font.d->dpi / qt_defaultDpiY(); @@ -1482,7 +1485,14 @@ void QTextEngine::shapeText(int item) const #if QT_CONFIG(harfbuzz) if (Q_LIKELY(shapingEnabled)) { - si.num_glyphs = shapeTextWithHarfbuzzNG(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled, letterSpacing != 0); + si.num_glyphs = shapeTextWithHarfbuzzNG(si, + string, + itemLength, + fontEngine, + itemBoundaries, + kerningEnabled, + letterSpacing != 0, + fontFeatures); } else #endif { @@ -1594,7 +1604,8 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, QFontEngine *fontEngine, const QList<uint> &itemBoundaries, bool kerningEnabled, - bool hasLetterSpacing) const + bool hasLetterSpacing, + const QHash<quint32, quint32> &fontFeatures) const { uint glyphs_shaped = 0; @@ -1648,14 +1659,24 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, || script == QChar::Script_Khmer || script == QChar::Script_Nko); bool dontLigate = hasLetterSpacing && !scriptRequiresOpenType; - const hb_feature_t features[5] = { - { HB_TAG('k','e','r','n'), !!kerningEnabled, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }, - { HB_TAG('l','i','g','a'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }, - { HB_TAG('c','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }, - { HB_TAG('d','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }, - { HB_TAG('h','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END } - }; - const int num_features = dontLigate ? 5 : 1; + + QHash<quint32, quint32> features; + features.insert(HB_TAG('k','e','r','n'), !!kerningEnabled); + if (dontLigate) { + features.insert(HB_TAG('l','i','g','a'), false); + features.insert(HB_TAG('c','l','i','g'), false); + features.insert(HB_TAG('d','l','i','g'), false); + features.insert(HB_TAG('h','l','i','g'), false); + } + features.insert(fontFeatures); + + QVarLengthArray<hb_feature_t, 16> featureArray; + for (auto it = features.constBegin(); it != features.constEnd(); ++it) { + featureArray.append({ it.key(), + it.value(), + HB_FEATURE_GLOBAL_START, + HB_FEATURE_GLOBAL_END }); + } // whitelist cross-platforms shapers only static const char *shaper_list[] = { @@ -1665,7 +1686,11 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, nullptr }; - bool shapedOk = hb_shape_full(hb_font, buffer, features, num_features, shaper_list); + bool shapedOk = hb_shape_full(hb_font, + buffer, + featureArray.constData(), + features.size(), + shaper_list); if (Q_UNLIKELY(!shapedOk)) { hb_buffer_destroy(buffer); return 0; diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 59e332c64a..a6015cc311 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -621,9 +621,14 @@ private: void addRequiredBoundaries() const; void shapeText(int item) const; #if QT_CONFIG(harfbuzz) - int shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *string, int itemLength, - QFontEngine *fontEngine, const QList<uint> &itemBoundaries, - bool kerningEnabled, bool hasLetterSpacing) const; + int shapeTextWithHarfbuzzNG(const QScriptItem &si, + const ushort *string, + int itemLength, + QFontEngine *fontEngine, + const QList<uint> &itemBoundaries, + bool kerningEnabled, + bool hasLetterSpacing, + const QHash<quint32, quint32> &fontFeatures) const; #endif int endOfLine(int lineNum); diff --git a/tests/manual/fontfeatures/fontfeatures.pro b/tests/manual/fontfeatures/fontfeatures.pro new file mode 100644 index 0000000000..62769072b4 --- /dev/null +++ b/tests/manual/fontfeatures/fontfeatures.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +TARGET = fontfeatures +INCLUDEPATH += . +QT += widgets + +# You can make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# Please consult the documentation of the deprecated API in order to know +# how to port your code away from it. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_UP_TO=0x060000 # disables all APIs deprecated in Qt 6.0.0 and earlier + +# Input +HEADERS += mainwindow.h +FORMS += mainwindow.ui +SOURCES += main.cpp \ + mainwindow.cpp \ diff --git a/tests/manual/fontfeatures/main.cpp b/tests/manual/fontfeatures/main.cpp new file mode 100644 index 0000000000..9c5bc17874 --- /dev/null +++ b/tests/manual/fontfeatures/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "mainwindow.h" + +#include <QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/manual/fontfeatures/mainwindow.cpp b/tests/manual/fontfeatures/mainwindow.cpp new file mode 100644 index 0000000000..5342f7c89b --- /dev/null +++ b/tests/manual/fontfeatures/mainwindow.cpp @@ -0,0 +1,225 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + setup(); + updateSampleText(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::updateSampleText() +{ + QFont font = ui->fontComboBox->currentFont(); + font.setPixelSize(54); + + for (int i = 0; i < ui->lwFeatures->count(); ++i) { + QListWidgetItem *it = ui->lwFeatures->item(i); + if (it->checkState() != Qt::PartiallyChecked) { + QByteArray ba = it->text().toLatin1(); + font.setFontFeature(ba, !!it->checkState()); + } + } + + ui->lSampleDisplay->setFont(font); + ui->lSampleDisplay->setText(ui->leSampleText->text()); +} + +void MainWindow::enableAll() +{ + for (int i = 0; i < ui->lwFeatures->count(); ++i) { + QListWidgetItem *it = ui->lwFeatures->item(i); + it->setCheckState(Qt::Checked); + } +} + +void MainWindow::disableAll() +{ + for (int i = 0; i < ui->lwFeatures->count(); ++i) { + QListWidgetItem *it = ui->lwFeatures->item(i); + it->setCheckState(Qt::Unchecked); + } +} + +void MainWindow::reset() +{ + for (int i = 0; i < ui->lwFeatures->count(); ++i) { + QListWidgetItem *it = ui->lwFeatures->item(i); + it->setCheckState(Qt::PartiallyChecked); + } +} + +void MainWindow::setup() +{ + connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, &MainWindow::updateSampleText); + connect(ui->leSampleText, &QLineEdit::textChanged, this, &MainWindow::updateSampleText); + connect(ui->lwFeatures, &QListWidget::itemChanged, this, &MainWindow::updateSampleText); + connect(ui->pbEnableAll, &QPushButton::clicked, this, &MainWindow::enableAll); + connect(ui->pbDisableAll, &QPushButton::clicked, this, &MainWindow::disableAll); + connect(ui->pbReset, &QPushButton::clicked, this, &MainWindow::reset); + + QList<QByteArray> featureList = + { + "aalt", + "abvf", + "abvm", + "abvs", + "afrc", + "akhn", + "blwf", + "blwm", + "blws", + "calt", + "case", + "ccmp", + "cfar", + "chws", + "cjct", + "clig", + "cpct", + "cpsp", + "cswh", + "curs", + "cv01", + "c2pc", + "c2sc", + "dist", + "dlig", + "dnom", + "dtls", + "expt", + "falt", + "fin2", + "fin3", + "fina", + "flac", + "frac", + "fwid", + "half", + "haln", + "halt", + "hist", + "hkna", + "hlig", + "hngl", + "hojo", + "hwid", + "init", + "isol", + "ital", + "jalt", + "jp78", + "jp83", + "jp90", + "jp04", + "kern", + "lfbd", + "liga", + "ljmo", + "lnum", + "locl", + "ltra", + "ltrm", + "mark", + "med2", + "medi", + "mgrk", + "mkmk", + "mset", + "nalt", + "nlck", + "nukt", + "numr", + "onum", + "opbd", + "ordn", + "ornm", + "palt", + "pcap", + "pkna", + "pnum", + "pref", + "pres", + "pstf", + "psts", + "pwid", + "qwid", + "rand", + "rclt", + "rkrf", + "rlig", + "rphf", + "rtbd", + "rtla", + "rtlm", + "ruby", + "rvrn", + "salt", + "sinf", + "size", + "smcp", + "smpl", + "ss01", + "ss02", + "ss03", + "ss04", + "ss05", + "ss06", + "ss07", + "ss08", + "ss09", + "ss10", + "ss11", + "ss12", + "ss13", + "ss14", + "ss15", + "ss16", + "ss17", + "ss18", + "ss19", + "ss20", + "ssty", + "stch", + "subs", + "sups", + "swsh", + "titl", + "tjmo", + "tnam", + "tnum", + "trad", + "twid", + "unic", + "valt", + "vatu", + "vchw", + "vert", + "vhal", + "vjmo", + "vkna", + "vkrn", + "vpal", + "vrt2", + "vrtr", + "zero" + }; + + for (auto it = featureList.constBegin(); it != featureList.constEnd(); ++it) { + QListWidgetItem *item = new QListWidgetItem(*it); + item->setFlags(Qt::ItemIsUserTristate | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + item->setCheckState(Qt::PartiallyChecked); + ui->lwFeatures->addItem(item); + } +} diff --git a/tests/manual/fontfeatures/mainwindow.h b/tests/manual/fontfeatures/mainwindow.h new file mode 100644 index 0000000000..c8248e7558 --- /dev/null +++ b/tests/manual/fontfeatures/mainwindow.h @@ -0,0 +1,32 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void updateSampleText(); + void enableAll(); + void disableAll(); + void reset(); + +private: + Ui::MainWindow *ui; + + void setup(); +}; +#endif // MAINWINDOW_H diff --git a/tests/manual/fontfeatures/mainwindow.ui b/tests/manual/fontfeatures/mainwindow.ui new file mode 100644 index 0000000000..17f56c5a01 --- /dev/null +++ b/tests/manual/fontfeatures/mainwindow.ui @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QFontComboBox" name="fontComboBox"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Sample text:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="leSampleText"> + <property name="text"> + <string>Foobar</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="pbEnableAll"> + <property name="text"> + <string>Enable all</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbDisableAll"> + <property name="text"> + <string>Disable all</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbReset"> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QListWidget" name="lwFeatures"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="flow"> + <enum>QListView::TopToBottom</enum> + </property> + <property name="isWrapping" stdset="0"> + <bool>false</bool> + </property> + <property name="viewMode"> + <enum>QListView::ListMode</enum> + </property> + <property name="uniformItemSizes"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="lSampleDisplay"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>LABEL</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>21</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 7bb92bb453..3519fc1148 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -5,6 +5,7 @@ SUBDIRS = \ filetest \ embeddedintoforeignwindow \ foreignwindows \ +fontfeatures \ gestures \ highdpi \ inputmethodhints \ |