9#include "kfontchooser.h"
10#include "fonthelpers_p.h"
11#include "ui_kfontchooserwidget.h"
13#include "loggingcategory.h"
16#include <QDoubleSpinBox>
17#include <QFontDatabase>
19#include <QGuiApplication>
43 for (
int i = 0, rows =
list->
count(); i < rows; ++i) {
46 itemWidth += extraSpace;
47 width = std::max(width, itemWidth);
50 width +=
list->frameWidth() * 2;
51 width +=
list->verticalScrollBar()->sizeHint().width();
55static int minimumListHeight(
const QListWidget *list,
int numVisibleEntry)
57 int w =
list->fontMetrics().lineSpacing();
62 if (numVisibleEntry <= 0) {
66 w = w * numVisibleEntry;
67 w +=
list->frameWidth() * 2;
68 w +=
list->horizontalScrollBar()->sizeHint().height();
72static QString formatFontSize(qreal size)
77class KFontChooserPrivate
79 Q_DECLARE_TR_FUNCTIONS(KFontChooser)
91 void setFamilyBoxItems(
const QStringList &fonts = {});
92 int nearestSizeRow(qreal val,
bool customize);
93 qreal fillSizeList(
const QList<qreal> &sizes = QList<qreal>());
94 qreal setupSizeListBox(
const QString &family,
const QString &style);
97 QString styleIdentifier(
const QFont &font);
99 void slotFamilySelected(
const QString &);
100 void slotSizeSelected(
const QString &);
101 void slotStyleSelected(
const QString &);
102 void displaySample(
const QFont &font);
103 void slotSizeValue(
double);
104 void slotFeaturesChanged(
const QString &features);
108 std::unique_ptr<Ui_KFontChooserWidget> m_ui;
114 QFont m_selectedFont;
116 QString m_selectedStyle;
117 qreal m_selectedSize = -1.0;
119 QString m_standardSizeAtCustom;
120 int m_customSizeRow = -1;
122 bool m_signalsAllowed =
true;
123 bool m_usingFixed =
false;
126 FontFamiliesMap m_qtFamilies;
127 std::map<QString, QString> m_qtStyles;
129 std::map<QString, QString> m_styleIDs;
131 QTimer m_fontFeatureChangedTimer;
143 , d(new KFontChooserPrivate(flags, this))
150void KFontChooserPrivate::init()
163 m_ui.reset(
new Ui_KFontChooserWidget);
166#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
167 m_ui->fontFeaturesLabel->setVisible(
false);
168 m_ui->fontFeaturesLineEdit->setVisible(
false);
179 m_ui->sizeIsRelativeCheckBox->hide();
184 slotFamilySelected(family);
188 m_ui->familyLabel->hide();
189 m_ui->familyListWidget->setEnabled(
false);
192 m_ui->familyCheckBox->hide();
198 m_ui->onlyFixedCheckBox->setVisible(!m_usingFixed);
200 if (!m_ui->onlyFixedCheckBox->isHidden()) {
202 q->setFont(m_selectedFont, state);
206 m_ui->onlyFixedCheckBox->setEnabled(
false);
217 m_ui->styleListWidget->addItem(
KFontChooser::tr(
"Bold Condensed Oblique",
"@item font"));
218 m_ui->styleListWidget->setMinimumWidth(minimumListWidth(m_ui->styleListWidget));
221 slotStyleSelected(style);
225 m_ui->styleLabel->hide();
226 m_ui->styleListWidget->setEnabled(
false);
229 m_ui->styleCheckBox->hide();
241 slotSizeSelected(size);
244 m_fontFeatureChangedTimer.setInterval(200);
245 m_fontFeatureChangedTimer.setSingleShot(
true);
246 m_fontFeatureChangedTimer.callOnTimeout([
this]() {
247 slotFeaturesChanged(m_ui->fontFeaturesLineEdit->text());
251 m_fontFeatureChangedTimer.start();
255 m_ui->sizeLabel->hide();
256 m_ui->sizeListWidget->setEnabled(
false);
257 m_ui->sizeSpinBox->setEnabled(
false);
261 m_ui->sizeCheckBox->hide();
265 m_ui->sampleTextEdit->setFont(tmpFont);
266 m_ui->sampleTextEdit->setMinimumHeight(m_ui->sampleTextEdit->fontMetrics().lineSpacing());
271 q->setSampleText(
KFontChooser::tr(
"The Quick Brown Fox Jumps Over The Lazy Dog"));
272 m_ui->sampleTextEdit->setTextCursor(QTextCursor(m_ui->sampleTextEdit->document()));
286 q->setMinVisibleItems(4);
289 m_ui->sizeListWidget->setFocus();
295 QPalette pal = d->m_ui->sampleTextEdit->palette();
297 d->m_ui->sampleTextEdit->setPalette(pal);
299 d->m_ui->sampleTextEdit->selectAll();
300 d->m_ui->sampleTextEdit->setTextColor(col);
301 d->m_ui->sampleTextEdit->setTextCursor(
cursor);
312 QPalette pal = d->m_ui->sampleTextEdit->palette();
314 d->m_ui->sampleTextEdit->setPalette(pal);
317QColor KFontChooser::backgroundColor()
const
324 return d->m_ui->sampleTextEdit->toPlainText();
329 d->m_ui->sampleTextEdit->setPlainText(text);
334 d->m_ui->sampleTextEdit->setVisible(
visible);
345 d->m_ui->familyListWidget->setEnabled(state);
348 d->m_ui->styleListWidget->setEnabled(state);
351 d->m_ui->sizeListWidget->setEnabled(state);
352 d->m_ui->sizeSpinBox->setEnabled(state);
358 d->m_selectedFont = aFont;
360 if (d->m_selectedSize == -1) {
364 if (onlyFixed != d->m_usingFixed) {
365 d->m_usingFixed = onlyFixed;
366 d->setFamilyBoxItems();
375 if (d->m_ui->familyCheckBox->isChecked()) {
379 if (d->m_ui->styleCheckBox->isChecked()) {
383 if (d->m_ui->sizeCheckBox->isChecked()) {
392 return d->m_selectedFont;
395static bool isDefaultFontStyleName(
const QString &style)
406void KFontChooserPrivate::slotFamilySelected(
const QString &family)
408 if (!m_signalsAllowed) {
411 m_signalsAllowed =
false;
413 QString currentFamily;
415 Q_ASSERT(m_ui->familyListWidget->currentItem());
416 if (m_ui->familyListWidget->currentItem()) {
417 currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
420 currentFamily = m_qtFamilies[family];
427 styles.
append(TR_NOX(
"Normal",
"QFontDatabase"));
432 std::sort(styles.
begin(), styles.
end(), [](
const QString &a,
const QString &b) {
433 if (isDefaultFontStyleName(a)) {
435 }
else if (isDefaultFontStyleName(b)) {
443 splitFontString(family, &pureFamily);
449 for (
const QString &style : origStyles) {
459 QString fstyle = tr(
"%1",
"@item Font style").
arg(style);
460 if (!filteredStyles.
contains(fstyle)) {
461 filteredStyles.
append(fstyle);
462 m_qtStyles.insert({fstyle, style});
463 m_styleIDs.insert({fstyle, styleIdentifier(testFont)});
466 m_ui->styleListWidget->
clear();
467 m_ui->styleListWidget->addItems(filteredStyles);
470 int listPos = filteredStyles.
indexOf(m_selectedStyle.isEmpty() ? TR_NOX(
"Normal",
"QFontDatabase") : m_selectedStyle);
474 QString styleIt = tr(
"Italic",
"@item font");
475 QString styleOb = tr(
"Oblique",
"@item font");
476 for (
int i = 0; i < 2; ++i) {
477 int pos = m_selectedStyle.indexOf(styleIt);
479 QString style = m_selectedStyle;
481 listPos = filteredStyles.
indexOf(style);
486 qSwap(styleIt, styleOb);
489 m_ui->styleListWidget->setCurrentRow(listPos >= 0 ? listPos : 0);
490 const QString currentStyle = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
493 qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
494 m_ui->sizeSpinBox->setValue(currentSize);
496 m_selectedFont =
QFontDatabase::font(currentFamily, currentStyle,
static_cast<int>(currentSize));
498 m_selectedFont.setPointSizeF(currentSize);
500 Q_EMIT q->fontSelected(m_selectedFont);
502 m_signalsAllowed =
true;
505void KFontChooserPrivate::slotStyleSelected(
const QString &style)
507 if (!m_signalsAllowed) {
510 m_signalsAllowed =
false;
512 const QString currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
513 const QString currentStyle = !style.
isEmpty() ? m_qtStyles[style] : m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
516 qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
517 m_ui->sizeSpinBox->setValue(currentSize);
519 m_selectedFont =
QFontDatabase::font(currentFamily, currentStyle,
static_cast<int>(currentSize));
521 m_selectedFont.setPointSizeF(currentSize);
523 Q_EMIT q->fontSelected(m_selectedFont);
526 m_selectedStyle = currentStyle;
529 m_signalsAllowed =
true;
532void KFontChooserPrivate::slotSizeSelected(
const QString &size)
534 if (!m_signalsAllowed) {
538 m_signalsAllowed =
false;
540 qreal currentSize = 0.0;
548 if (m_customSizeRow >= 0 && m_selectedFont.pointSizeF() != currentSize) {
549 m_ui->sizeListWidget->item(m_customSizeRow)->setText(m_standardSizeAtCustom);
550 m_customSizeRow = -1;
553 m_ui->sizeSpinBox->setValue(currentSize);
554 m_selectedFont.setPointSizeF(currentSize);
555 Q_EMIT q->fontSelected(m_selectedFont);
558 m_selectedSize = currentSize;
561 m_signalsAllowed =
true;
564void KFontChooserPrivate::slotSizeValue(
double dval)
566 if (!m_signalsAllowed) {
569 m_signalsAllowed =
false;
572 qreal val = qreal(dval);
575 if (m_customSizeRow >= 0 && m_ui->sizeListWidget->currentRow() == m_customSizeRow) {
576 m_ui->sizeListWidget->item(m_customSizeRow)->setText(m_standardSizeAtCustom);
577 m_customSizeRow = -1;
580 bool canCustomize =
true;
582 const QString family = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
583 const QString style = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
589 canCustomize =
false;
590 int nrows = m_ui->sizeListWidget->count();
591 int row = m_ui->sizeListWidget->currentRow();
593 if (val - m_selectedFont.pointSizeF() > 0) {
594 for (nrow = row + 1; nrow < nrows; ++nrow) {
595 if (
QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text()) >= val) {
600 for (nrow = row - 1; nrow >= 0; --nrow) {
601 if (
QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text()) <= val) {
607 nrow = nrow < 0 ? 0 : nrow >= nrows ? nrows - 1 : nrow;
610 m_ui->sizeSpinBox->setValue(val);
614 int row = nearestSizeRow(val, canCustomize);
615 m_ui->sizeListWidget->setCurrentRow(row);
617 m_selectedSize = val;
618 m_selectedFont.setPointSizeF(val);
619 Q_EMIT q->fontSelected(m_selectedFont);
621 m_signalsAllowed =
true;
624void KFontChooserPrivate::slotFeaturesChanged(
const QString &features)
626#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
627 m_selectedFont.clearFeatures();
634 for (
const QString &feature : rawFeaturesList) {
635 auto f = QStringView(feature).trimmed();
640 if (parts.
length() == 2) {
641 const auto tag = QFont::Tag::fromString(parts[0]);
643 const int number = parts[1].toInt(&ok);
644 if (tag.has_value() && ok) {
645 m_selectedFont.setFeature(tag.value(), number);
647 }
else if (f.size() <= 4) {
648 const auto tag = QFont::Tag::fromString(feature);
649 if (tag.has_value()) {
650 m_selectedFont.setFeature(tag.value(), 1);
655 Q_EMIT q->fontSelected(m_selectedFont);
659void KFontChooserPrivate::displaySample(
const QFont &font)
661 m_ui->sampleTextEdit->setFont(font);
664int KFontChooserPrivate::nearestSizeRow(qreal val,
bool customize)
668 for (
int r = 0; r < m_ui->sizeListWidget->count(); ++r) {
670 if (qAbs(cval - val) < diff) {
671 diff = qAbs(cval - val);
676 if (customize && diff > 0) {
677 m_customSizeRow = row;
678 m_standardSizeAtCustom = m_ui->sizeListWidget->item(row)->text();
679 m_ui->sizeListWidget->item(row)->setText(formatFontSize(val));
684qreal KFontChooserPrivate::fillSizeList(
const QList<qreal> &sizes_)
686 if (m_ui->sizeListWidget->isHidden()) {
687 qCWarning(KWidgetsAddonsLog) <<
"Trying to fill the font size listwidget, but the widget is hidden.";
691 QList<qreal> sizes = sizes_;
692 bool canCustomize =
false;
694 static const int c[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 32, 48, 64, 72, 80, 96, 128, 0};
695 for (
int i = 0; c[i]; ++i) {
704 m_ui->sizeListWidget->clear();
705 std::sort(sizes.
begin(), sizes.
end());
706 for (qreal size : std::as_const(sizes)) {
707 m_ui->sizeListWidget->addItem(formatFontSize(size));
715 m_customSizeRow = -1;
716 int row = nearestSizeRow(m_selectedSize, canCustomize);
720qreal KFontChooserPrivate::setupSizeListBox(
const QString &family,
const QString &style)
724 if (!smoothlyScalable) {
726 for (
int size : smoothSizes) {
733 qreal bestFitSize = fillSizeList(sizes);
736 const QList<QListWidgetItem *> selectedSizeList = m_ui->sizeListWidget->findItems(formatFontSize(bestFitSize),
Qt::MatchExactly);
737 if (!selectedSizeList.
isEmpty()) {
738 m_ui->sizeListWidget->setCurrentItem(selectedSizeList.
first());
744void KFontChooserPrivate::setupDisplay()
746 qreal size = m_selectedFont.pointSizeF();
748 size = QFontInfo(m_selectedFont).pointSizeF();
752#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
753 const auto tags = m_selectedFont.featureTags();
755 for (
const auto &tag : tags) {
757 const quint32 value = m_selectedFont.featureValue(tag);
764 m_ui->fontFeaturesLineEdit->setText(
features.join(QStringLiteral(
",")));
772 const QString styleID = styleIdentifier(m_selectedFont);
774 QString family = m_selectedFont.family().
toLower();
776 numEntries = m_ui->familyListWidget->count();
777 for (i = 0; i < numEntries; ++i) {
778 if (family == m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower()) {
779 m_ui->familyListWidget->setCurrentRow(i);
785 if (i == numEntries) {
786 const int bracketPos = family.
indexOf(QLatin1Char(
'['));
787 if (bracketPos != -1) {
788 family = QStringView(family).
left(bracketPos).
trimmed().toString();
789 for (i = 0; i < numEntries; ++i) {
790 if (family == m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower()) {
791 m_ui->familyListWidget->setCurrentRow(i);
799 if (i == numEntries) {
800 QString fallback = family + QLatin1String(
" [");
801 for (i = 0; i < numEntries; ++i) {
802 if (m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower().startsWith(fallback)) {
803 m_ui->familyListWidget->setCurrentRow(i);
810 if (i == numEntries) {
811 for (i = 0; i < numEntries; ++i) {
812 if (m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower().startsWith(family)) {
813 m_ui->familyListWidget->setCurrentRow(i);
820 if (i == numEntries) {
821 m_ui->familyListWidget->setCurrentRow(0);
829 numEntries = m_ui->styleListWidget->count();
830 for (i = 0; i < numEntries; ++i) {
831 if (styleID == m_styleIDs[m_ui->styleListWidget->item(i)->text()]) {
832 m_ui->styleListWidget->setCurrentRow(i);
836 if (i == numEntries) {
838 m_ui->styleListWidget->setCurrentRow(0);
844 const QString currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
845 const QString currentStyle = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
847 m_ui->sizeListWidget->setCurrentRow(nearestSizeRow(size, canCustomize));
850 m_ui->sizeSpinBox->setValue(
QLocale::system().toDouble(m_ui->sizeListWidget->currentItem()->text()));
859 if (fontListCriteria) {
861 for (
const QString &family : std::as_const(lstSys)) {
878 lstFonts.
append(QStringLiteral(
"fixed"));
892 d->setFamilyBoxItems(fontList);
895void KFontChooserPrivate::setFamilyBoxItems(
const QStringList &fonts)
897 m_signalsAllowed =
false;
899 m_ui->familyListWidget->clear();
904 list.
reserve(m_qtFamilies.size());
908 translateFontName(QStringLiteral(
"Sans Serif")),
909 translateFontName(QStringLiteral(
"Serif")),
910 translateFontName(QStringLiteral(
"Monospace")),
914 for (
const QString &s : genericTranslatedNames) {
915 auto nIt = m_qtFamilies.find(s);
916 if (nIt != m_qtFamilies.cend()) {
921 for (
auto it = m_qtFamilies.cbegin(); it != m_qtFamilies.cend(); ++it) {
923 if (genericTranslatedNames.contains(name)) {
930 m_ui->familyListWidget->addItems(list);
931 m_ui->familyListWidget->setMinimumWidth(minimumListWidth(m_ui->familyListWidget));
933 m_signalsAllowed =
true;
938 for (
auto *widget : {d->m_ui->familyListWidget, d->m_ui->styleListWidget, d->m_ui->sizeListWidget}) {
939 widget->setMinimumHeight(minimumListHeight(widget, visibleItems));
949QString KFontChooserPrivate::styleIdentifier(
const QFont &font)
951 const int weight = font.
weight();
961 for (
const QString &style : styles) {
962 if (isDefaultFontStyleName(style)) {
971 const QChar comma(QLatin1Char(
','));
978#include "moc_kfontchooser.cpp"
@ FamilyList
Identifies the family (leftmost) list.
@ SizeList
Identifies the size (rightmost) list.
@ StyleList
Identifies the style (center) list.
QFlags< DisplayFlag > DisplayFlags
Stores a combination of DisplayFlag values.
void setColor(const QColor &col)
Sets the color to use for the font in the preview area.
void setBackgroundColor(const QColor &col)
Sets the background color to use in the preview area.
void setSampleBoxVisible(bool visible)
If visible is true the preview area will be shown, and vice-versa is it's false.
~KFontChooser() override
Destructor.
QSize sizeHint(void) const override
Reimplemented for internal reasons.
@ FixedWidthFonts
If set, only show fixed fixed-width (monospace) fonts.
@ ScalableFonts
If set, only show scalable fonts.
@ SmoothScalableFonts
If set, only show smooth scalable fonts.
@ DisplayFrame
Show a visual frame around the chooser.
@ ShowDifferences
Display the font differences interfaces.
@ FixedFontsOnly
Only show monospaced/fixed-width fonts, excluding proportional fonts, (the checkbox to toggle showing...
@ NoDisplayFlags
No flags set.
void fontSelected(const QFont &font)
Emitted when the selected font changes.
void setSampleText(const QString &text)
Sets the sample text in the preview area; this is useful if you want to use text in your native langu...
FontDiffFlags fontDiffFlags() const
Returns the bitmask corresponding to the attributes the user wishes to change.
void setMinVisibleItems(int visibleItems)
Sets the minimum number of items that should be visible in the child list widgets; this number will b...
KFontChooser(QWidget *parent=nullptr)
Constructs a font picker widget.
void setFont(const QFont &font, bool onlyFixed=false)
Sets the currently selected font in the widget.
void enableColumn(int column, bool state)
Enables or disables a column (family, style, size) in the widget.
@ NoFontDiffFlags
No flags set.
@ FontDiffFamily
Identifies a requested change in the font family.
@ FontDiffStyle
Identifies a requested change in the font style.
@ FontDiffSize
Identifies a requested change in the font size.
void setFontListItems(const QStringList &fontList)
Uses fontList to fill the font family list in the widget.
QFlags< FontDiff > FontDiffFlags
Stores an combination of FontDiff values.
static QStringList createFontList(uint fontListCriteria)
Returns a list of font faimly name strings filtered based on fontListCriteria.
KIOCORE_EXPORT QString number(KIO::filesize_t size)
QString name(const QVariant &location)
std::vector< Feature > features(QStringView coachNumber, QStringView coachClassification)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem ok()
Returns the 'Ok' gui item.
void valueChanged(double d)
QString family() const const
qreal pointSizeF() const const
int stretch() const const
Style style() const const
QString styleName() const const
Weight weight() const const
QStringList families(WritingSystem writingSystem)
QFont font(const QString &family, const QString &style, int pointSize)
bool isBitmapScalable(const QString &family, const QString &style)
bool isFixedPitch(const QString &family, const QString &style)
bool isSmoothlyScalable(const QString &family, const QString &style)
QList< int > smoothSizes(const QString &family, const QString &styleName)
QString styleString(const QFont &font)
QStringList styles(const QString &family)
QFont systemFont(SystemFont type)
qreal pointSizeF() const const
int horizontalAdvance(QChar ch) const const
void setContentsMargins(const QMargins &margins)
void textChanged(const QString &text)
void append(QList< T > &&value)
qsizetype count() const const
bool isEmpty() const const
qsizetype length() const const
void push_back(parameter_type value)
qsizetype removeAll(const AT &t)
void reserve(qsizetype size)
void currentTextChanged(const QString ¤tText)
double toDouble(QStringView s, bool *ok) const const
QString toString(QDate date, FormatType format) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QString arg(Args &&... args) const const
QString first(qsizetype n) const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString toLower() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
void sort(Qt::CaseSensitivity cs)
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0