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);
174 m_ui->sizeIsRelativeCheckBox->hide();
179 slotFamilySelected(family);
183 m_ui->familyLabel->hide();
184 m_ui->familyListWidget->setEnabled(
false);
187 m_ui->familyCheckBox->hide();
193 m_ui->onlyFixedCheckBox->setVisible(!m_usingFixed);
195 if (!m_ui->onlyFixedCheckBox->isHidden()) {
197 q->setFont(m_selectedFont, state);
201 m_ui->onlyFixedCheckBox->setEnabled(
false);
212 m_ui->styleListWidget->addItem(
KFontChooser::tr(
"Bold Condensed Oblique",
"@item font"));
213 m_ui->styleListWidget->setMinimumWidth(minimumListWidth(m_ui->styleListWidget));
216 slotStyleSelected(style);
220 m_ui->styleLabel->hide();
221 m_ui->styleListWidget->setEnabled(
false);
224 m_ui->styleCheckBox->hide();
236 slotSizeSelected(size);
239 m_fontFeatureChangedTimer.setInterval(200);
240 m_fontFeatureChangedTimer.setSingleShot(
true);
241 m_fontFeatureChangedTimer.callOnTimeout([
this]() {
242 slotFeaturesChanged(m_ui->fontFeaturesLineEdit->text());
246 m_fontFeatureChangedTimer.start();
250 m_ui->sizeLabel->hide();
251 m_ui->sizeListWidget->setEnabled(
false);
252 m_ui->sizeSpinBox->setEnabled(
false);
256 m_ui->sizeCheckBox->hide();
260 m_ui->sampleTextEdit->setFont(tmpFont);
261 m_ui->sampleTextEdit->setMinimumHeight(m_ui->sampleTextEdit->fontMetrics().lineSpacing());
266 q->setSampleText(
KFontChooser::tr(
"The Quick Brown Fox Jumps Over The Lazy Dog"));
267 m_ui->sampleTextEdit->setTextCursor(QTextCursor(m_ui->sampleTextEdit->document()));
281 q->setMinVisibleItems(4);
284 m_ui->sizeListWidget->setFocus();
290 QPalette pal = d->m_ui->sampleTextEdit->palette();
292 d->m_ui->sampleTextEdit->setPalette(pal);
294 d->m_ui->sampleTextEdit->selectAll();
295 d->m_ui->sampleTextEdit->setTextColor(col);
296 d->m_ui->sampleTextEdit->setTextCursor(
cursor);
307 QPalette pal = d->m_ui->sampleTextEdit->palette();
309 d->m_ui->sampleTextEdit->setPalette(pal);
312QColor KFontChooser::backgroundColor()
const
319 return d->m_ui->sampleTextEdit->toPlainText();
324 d->m_ui->sampleTextEdit->setPlainText(text);
329 d->m_ui->sampleTextEdit->setVisible(
visible);
340 d->m_ui->familyListWidget->setEnabled(state);
343 d->m_ui->styleListWidget->setEnabled(state);
346 d->m_ui->sizeListWidget->setEnabled(state);
347 d->m_ui->sizeSpinBox->setEnabled(state);
353 d->m_selectedFont = aFont;
355 if (d->m_selectedSize == -1) {
359 if (onlyFixed != d->m_usingFixed) {
360 d->m_usingFixed = onlyFixed;
361 d->setFamilyBoxItems();
370 if (d->m_ui->familyCheckBox->isChecked()) {
374 if (d->m_ui->styleCheckBox->isChecked()) {
378 if (d->m_ui->sizeCheckBox->isChecked()) {
387 return d->m_selectedFont;
390static bool isDefaultFontStyleName(
const QString &style)
401void KFontChooserPrivate::slotFamilySelected(
const QString &family)
403 if (!m_signalsAllowed) {
406 m_signalsAllowed =
false;
408 QString currentFamily;
410 Q_ASSERT(m_ui->familyListWidget->currentItem());
411 if (m_ui->familyListWidget->currentItem()) {
412 currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
415 currentFamily = m_qtFamilies[family];
422 styles.
append(TR_NOX(
"Normal",
"QFontDatabase"));
427 std::sort(styles.
begin(), styles.
end(), [](
const QString &a,
const QString &b) {
428 if (isDefaultFontStyleName(a)) {
430 }
else if (isDefaultFontStyleName(b)) {
438 splitFontString(family, &pureFamily);
444 for (
const QString &style : origStyles) {
454 QString fstyle = tr(
"%1",
"@item Font style").
arg(style);
455 if (!filteredStyles.
contains(fstyle)) {
456 filteredStyles.
append(fstyle);
457 m_qtStyles.insert({fstyle, style});
458 m_styleIDs.insert({fstyle, styleIdentifier(testFont)});
461 m_ui->styleListWidget->
clear();
462 m_ui->styleListWidget->addItems(filteredStyles);
465 int listPos = filteredStyles.
indexOf(m_selectedStyle.isEmpty() ? TR_NOX(
"Normal",
"QFontDatabase") : m_selectedStyle);
469 QString styleIt = tr(
"Italic",
"@item font");
470 QString styleOb = tr(
"Oblique",
"@item font");
471 for (
int i = 0; i < 2; ++i) {
472 int pos = m_selectedStyle.indexOf(styleIt);
474 QString style = m_selectedStyle;
476 listPos = filteredStyles.
indexOf(style);
481 qSwap(styleIt, styleOb);
484 m_ui->styleListWidget->setCurrentRow(listPos >= 0 ? listPos : 0);
485 const QString currentStyle = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
488 qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
489 m_ui->sizeSpinBox->setValue(currentSize);
491 m_selectedFont =
QFontDatabase::font(currentFamily, currentStyle,
static_cast<int>(currentSize));
493 m_selectedFont.setPointSizeF(currentSize);
495 Q_EMIT q->fontSelected(m_selectedFont);
497 m_signalsAllowed =
true;
500void KFontChooserPrivate::slotStyleSelected(
const QString &style)
502 if (!m_signalsAllowed) {
505 m_signalsAllowed =
false;
507 const QString currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
508 const QString currentStyle = !style.
isEmpty() ? m_qtStyles[style] : m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
511 qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
512 m_ui->sizeSpinBox->setValue(currentSize);
514 m_selectedFont =
QFontDatabase::font(currentFamily, currentStyle,
static_cast<int>(currentSize));
516 m_selectedFont.setPointSizeF(currentSize);
518 Q_EMIT q->fontSelected(m_selectedFont);
521 m_selectedStyle = currentStyle;
524 m_signalsAllowed =
true;
527void KFontChooserPrivate::slotSizeSelected(
const QString &size)
529 if (!m_signalsAllowed) {
533 m_signalsAllowed =
false;
535 qreal currentSize = 0.0;
543 if (m_customSizeRow >= 0 && m_selectedFont.pointSizeF() != currentSize) {
544 m_ui->sizeListWidget->item(m_customSizeRow)->setText(m_standardSizeAtCustom);
545 m_customSizeRow = -1;
548 m_ui->sizeSpinBox->setValue(currentSize);
549 m_selectedFont.setPointSizeF(currentSize);
550 Q_EMIT q->fontSelected(m_selectedFont);
553 m_selectedSize = currentSize;
556 m_signalsAllowed =
true;
559void KFontChooserPrivate::slotSizeValue(
double dval)
561 if (!m_signalsAllowed) {
564 m_signalsAllowed =
false;
567 qreal val = qreal(dval);
570 if (m_customSizeRow >= 0 && m_ui->sizeListWidget->currentRow() == m_customSizeRow) {
571 m_ui->sizeListWidget->item(m_customSizeRow)->setText(m_standardSizeAtCustom);
572 m_customSizeRow = -1;
575 bool canCustomize =
true;
577 const QString family = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
578 const QString style = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
584 canCustomize =
false;
585 int nrows = m_ui->sizeListWidget->count();
586 int row = m_ui->sizeListWidget->currentRow();
588 if (val - m_selectedFont.pointSizeF() > 0) {
589 for (nrow = row + 1; nrow < nrows; ++nrow) {
590 if (
QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text()) >= val) {
595 for (nrow = row - 1; nrow >= 0; --nrow) {
596 if (
QLocale::system().toDouble(m_ui->sizeListWidget->item(nrow)->text()) <= val) {
602 nrow = nrow < 0 ? 0 : nrow >= nrows ? nrows - 1 : nrow;
605 m_ui->sizeSpinBox->setValue(val);
609 int row = nearestSizeRow(val, canCustomize);
610 m_ui->sizeListWidget->setCurrentRow(row);
612 m_selectedSize = val;
613 m_selectedFont.setPointSizeF(val);
614 Q_EMIT q->fontSelected(m_selectedFont);
616 m_signalsAllowed =
true;
619void KFontChooserPrivate::slotFeaturesChanged(
const QString &features)
621 m_selectedFont.clearFeatures();
628 for (
const QString &feature : rawFeaturesList) {
629 auto f = QStringView(feature).trimmed();
634 if (parts.
length() == 2) {
635 const auto tag = QFont::Tag::fromString(parts[0]);
637 const int number = parts[1].toInt(&ok);
638 if (tag.has_value() && ok) {
639 m_selectedFont.setFeature(tag.value(), number);
641 }
else if (f.size() <= 4) {
642 const auto tag = QFont::Tag::fromString(feature);
643 if (tag.has_value()) {
644 m_selectedFont.setFeature(tag.value(), 1);
649 Q_EMIT q->fontSelected(m_selectedFont);
652void KFontChooserPrivate::displaySample(
const QFont &font)
654 m_ui->sampleTextEdit->setFont(font);
657int KFontChooserPrivate::nearestSizeRow(qreal val,
bool customize)
661 for (
int r = 0; r < m_ui->sizeListWidget->count(); ++r) {
663 if (qAbs(cval - val) < diff) {
664 diff = qAbs(cval - val);
669 if (customize && diff > 0) {
670 m_customSizeRow = row;
671 m_standardSizeAtCustom = m_ui->sizeListWidget->item(row)->text();
672 m_ui->sizeListWidget->item(row)->setText(formatFontSize(val));
677qreal KFontChooserPrivate::fillSizeList(
const QList<qreal> &sizes_)
679 if (m_ui->sizeListWidget->isHidden()) {
680 qCWarning(KWidgetsAddonsLog) <<
"Trying to fill the font size listwidget, but the widget is hidden.";
684 QList<qreal> sizes = sizes_;
685 bool canCustomize =
false;
687 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};
688 for (
int i = 0; c[i]; ++i) {
697 m_ui->sizeListWidget->clear();
698 std::sort(sizes.
begin(), sizes.
end());
699 for (qreal size : std::as_const(sizes)) {
700 m_ui->sizeListWidget->addItem(formatFontSize(size));
708 m_customSizeRow = -1;
709 int row = nearestSizeRow(m_selectedSize, canCustomize);
713qreal KFontChooserPrivate::setupSizeListBox(
const QString &family,
const QString &style)
717 if (!smoothlyScalable) {
719 for (
int size : smoothSizes) {
726 qreal bestFitSize = fillSizeList(sizes);
729 const QList<QListWidgetItem *> selectedSizeList = m_ui->sizeListWidget->findItems(formatFontSize(bestFitSize),
Qt::MatchExactly);
730 if (!selectedSizeList.
isEmpty()) {
731 m_ui->sizeListWidget->setCurrentItem(selectedSizeList.
first());
737void KFontChooserPrivate::setupDisplay()
739 qreal size = m_selectedFont.pointSizeF();
741 size = QFontInfo(m_selectedFont).pointSizeF();
745 const auto tags = m_selectedFont.featureTags();
747 for (
const auto &tag : tags) {
749 const quint32 value = m_selectedFont.featureValue(tag);
756 m_ui->fontFeaturesLineEdit->setText(
features.join(QStringLiteral(
",")));
763 const QString styleID = styleIdentifier(m_selectedFont);
765 QString family = m_selectedFont.family().
toLower();
767 numEntries = m_ui->familyListWidget->count();
768 for (i = 0; i < numEntries; ++i) {
769 if (family == m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower()) {
770 m_ui->familyListWidget->setCurrentRow(i);
776 if (i == numEntries) {
777 const int bracketPos = family.
indexOf(QLatin1Char(
'['));
778 if (bracketPos != -1) {
779 family = QStringView(family).
left(bracketPos).
trimmed().toString();
780 for (i = 0; i < numEntries; ++i) {
781 if (family == m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower()) {
782 m_ui->familyListWidget->setCurrentRow(i);
790 if (i == numEntries) {
791 QString fallback = family + QLatin1String(
" [");
792 for (i = 0; i < numEntries; ++i) {
793 if (m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower().startsWith(fallback)) {
794 m_ui->familyListWidget->setCurrentRow(i);
801 if (i == numEntries) {
802 for (i = 0; i < numEntries; ++i) {
803 if (m_qtFamilies[m_ui->familyListWidget->item(i)->text()].toLower().startsWith(family)) {
804 m_ui->familyListWidget->setCurrentRow(i);
811 if (i == numEntries) {
812 m_ui->familyListWidget->setCurrentRow(0);
820 numEntries = m_ui->styleListWidget->count();
821 for (i = 0; i < numEntries; ++i) {
822 if (styleID == m_styleIDs[m_ui->styleListWidget->item(i)->text()]) {
823 m_ui->styleListWidget->setCurrentRow(i);
827 if (i == numEntries) {
829 m_ui->styleListWidget->setCurrentRow(0);
835 const QString currentFamily = m_qtFamilies[m_ui->familyListWidget->currentItem()->text()];
836 const QString currentStyle = m_qtStyles[m_ui->styleListWidget->currentItem()->text()];
838 m_ui->sizeListWidget->setCurrentRow(nearestSizeRow(size, canCustomize));
841 m_ui->sizeSpinBox->setValue(
QLocale::system().toDouble(m_ui->sizeListWidget->currentItem()->text()));
850 if (fontListCriteria) {
852 for (
const QString &family : std::as_const(lstSys)) {
869 lstFonts.
append(QStringLiteral(
"fixed"));
883 d->setFamilyBoxItems(fontList);
886void KFontChooserPrivate::setFamilyBoxItems(
const QStringList &fonts)
888 m_signalsAllowed =
false;
890 m_ui->familyListWidget->clear();
895 list.
reserve(m_qtFamilies.size());
899 translateFontName(QStringLiteral(
"Sans Serif")),
900 translateFontName(QStringLiteral(
"Serif")),
901 translateFontName(QStringLiteral(
"Monospace")),
905 for (
const QString &s : genericTranslatedNames) {
906 auto nIt = m_qtFamilies.find(s);
907 if (nIt != m_qtFamilies.cend()) {
912 for (
auto it = m_qtFamilies.cbegin(); it != m_qtFamilies.cend(); ++it) {
914 if (genericTranslatedNames.contains(name)) {
921 m_ui->familyListWidget->addItems(list);
922 m_ui->familyListWidget->setMinimumWidth(minimumListWidth(m_ui->familyListWidget));
924 m_signalsAllowed =
true;
929 for (
auto *widget : {d->m_ui->familyListWidget, d->m_ui->styleListWidget, d->m_ui->sizeListWidget}) {
930 widget->setMinimumHeight(minimumListHeight(widget, visibleItems));
940QString KFontChooserPrivate::styleIdentifier(
const QFont &font)
942 const int weight = font.
weight();
952 for (
const QString &style : styles) {
953 if (isDefaultFontStyleName(style)) {
962 const QChar comma(QLatin1Char(
','));
969#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)
std::vector< Feature > features(QStringView coachNumber, QStringView coachClassification)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString name(StandardAction id)
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