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>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2023-03-23 12:45:46 +01:00
parent b1d59d6dd9
commit 6160ea45b6
11 changed files with 673 additions and 17 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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; }
};

View File

@ -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;

View File

@ -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);

View File

@ -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 \

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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>

View File

@ -5,6 +5,7 @@ SUBDIRS = \
filetest \
embeddedintoforeignwindow \
foreignwindows \
fontfeatures \
gestures \
highdpi \
inputmethodhints \