diff --git a/src/kfontchooser.cpp b/src/kfontchooser.cpp index 4544491..80548fd 100644 --- a/src/kfontchooser.cpp +++ b/src/kfontchooser.cpp @@ -1,1028 +1,1028 @@ /* Copyright (C) 1996 Bernd Johannes Wuebben Copyright (c) 1999 Preston Brown Copyright (c) 1999 Mario Weilguni This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfontchooser.h" #include "fonthelpers_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // When message extraction needs to be avoided. #define TR_NOX tr static int minimumListWidth(const QListWidget *list) { int w = 0; for (int i = 0; i < list->count(); i++) { int itemWidth = list->visualItemRect(list->item(i)).width(); // ...and add a space on both sides for not too tight look. itemWidth += list->fontMetrics().horizontalAdvance(QLatin1Char(' ')) * 2; w = qMax(w, itemWidth); } if (w == 0) { w = 40; } w += list->frameWidth() * 2; w += list->verticalScrollBar()->sizeHint().width(); return w; } static int minimumListHeight(const QListWidget *list, int numVisibleEntry) { int w = list->count() > 0 ? list->visualItemRect(list->item(0)).height() : list->fontMetrics().lineSpacing(); if (w < 0) { w = 10; } if (numVisibleEntry <= 0) { numVisibleEntry = 4; } return (w * numVisibleEntry + 2 * list->frameWidth()); } static QString formatFontSize(qreal size) { return QLocale::system().toString(size, 'f', (size == floor(size)) ? 0 : 1); } class Q_DECL_HIDDEN KFontChooser::Private { public: Private(KFontChooser *qq) : q(qq) { m_palette.setColor(QPalette::Active, QPalette::Text, Qt::black); m_palette.setColor(QPalette::Active, QPalette::Base, Qt::white); signalsAllowed = true; selectedSize = -1; customSizeRow = -1; } // pointer to an optinally supplied list of fonts to // inserted into the fontdialog font-family combo-box // QStringList fontList; void init(const DisplayFlags &flags, const QStringList &fontList, int visibleListSize, Qt::CheckState *sizeIsRelativeState); void setFamilyBoxItems(const QStringList &fonts); void fillFamilyListBox(bool onlyFixedFonts = false); int nearestSizeRow(qreal val, bool customize); qreal fillSizeList(const QList &sizes = QList()); qreal setupSizeListBox(const QString &family, const QString &style); void setupDisplay(); QString styleIdentifier(const QFont &font); void _k_family_chosen_slot(const QString &); void _k_size_chosen_slot(const QString &); void _k_style_chosen_slot(const QString &); void _k_displaySample(const QFont &font); void _k_size_value_slot(double); KFontChooser *q; QPalette m_palette; QDoubleSpinBox *sizeOfFont = nullptr; QTextEdit *sampleEdit = nullptr; QLabel *familyLabel = nullptr; QLabel *styleLabel = nullptr; QCheckBox *familyCheckbox = nullptr; QCheckBox *styleCheckbox = nullptr; QCheckBox *sizeCheckbox = nullptr; QLabel *sizeLabel = nullptr; QListWidget *familyListBox = nullptr; QListWidget *styleListBox = nullptr; QListWidget *sizeListBox = nullptr; QCheckBox *sizeIsRelativeCheckBox = nullptr; QCheckBox *onlyFixedCheckbox = nullptr; QFont selFont; QString selectedStyle; qreal selectedSize; QString standardSizeAtCustom; int customSizeRow; bool signalsAllowed: 1; bool usingFixed: 1; // Mappings of translated to Qt originated family and style strings. QHash qtFamilies; QHash qtStyles; // Mapping of translated style strings to internal style identifiers. QHash styleIDs; }; KFontChooser::KFontChooser(QWidget *parent, const DisplayFlags &flags, const QStringList &fontList, int visibleListSize, Qt::CheckState *sizeIsRelativeState) : QWidget(parent), d(new KFontChooser::Private(this)) { d->init(flags, fontList, visibleListSize, sizeIsRelativeState); } KFontChooser::~KFontChooser() { delete d; } void KFontChooser::Private::init(const DisplayFlags &flags, const QStringList &fontList, int visibleListSize, Qt::CheckState *sizeIsRelativeState) { usingFixed = flags & FixedFontsOnly; // The main layout is divided horizontally into a top part with // the font attribute widgets (family, style, size) and a bottom // part with a preview of the selected font QVBoxLayout *mainLayout = new QVBoxLayout(q); mainLayout->setContentsMargins(0, 0, 0, 0); const int spacingHint = q->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); int checkBoxGap = spacingHint / 2; // Build the grid of font attribute widgets for the upper part of mainLayout QWidget *page; QGridLayout *gridLayout; int row = flags & DisplayFrame ? 1 : 0; if (flags & DisplayFrame) { page = new QGroupBox(KFontChooser::tr("Requested Font"), q); mainLayout->addWidget(page); gridLayout = new QGridLayout(page); } else { page = new QWidget(q); mainLayout->addWidget(page); gridLayout = new QGridLayout(page); gridLayout->setContentsMargins(0, 0, 0, 0); } // set up the family list view and checkbox QHBoxLayout *familyLayout = new QHBoxLayout(); familyLayout->addSpacing(checkBoxGap); if (flags & ShowDifferences) { familyCheckbox = new QCheckBox(KFontChooser::tr("Font", "@option:check"), page); familyLayout->addWidget(familyCheckbox, 0, Qt::AlignLeft); familyLabel = nullptr; } else { familyCheckbox = nullptr; familyLabel = new QLabel(KFontChooser::tr("Font:", "@label"), page); familyLayout->addWidget(familyLabel, 1, Qt::AlignLeft); } gridLayout->addLayout(familyLayout, row, 0); ++row; familyListBox = new QListWidget(page); gridLayout->addWidget(familyListBox, row, 0); connect(familyListBox, &QListWidget::currentTextChanged, [this](const QString &family) { _k_family_chosen_slot(family); }); if (flags & ShowDifferences) { familyListBox->setEnabled(false); connect(familyCheckbox, &QAbstractButton::toggled, familyListBox, &QWidget::setEnabled); } if (!fontList.isEmpty()) { setFamilyBoxItems(fontList); } else { fillFamilyListBox(flags & FixedFontsOnly); } familyListBox->setMinimumWidth(minimumListWidth(familyListBox)); familyListBox->setMinimumHeight(minimumListHeight(familyListBox, visibleListSize)); // set up the sytle list view and checkbox row = flags & DisplayFrame ? 1 : 0; QHBoxLayout *styleLayout = new QHBoxLayout(); if (flags & ShowDifferences) { styleCheckbox = new QCheckBox(KFontChooser::tr("Font style", "@option:check"), page); styleLayout->addWidget(styleCheckbox, 0, Qt::AlignLeft); styleLabel = nullptr; } else { styleCheckbox = nullptr; styleLabel = new QLabel(KFontChooser::tr("Font style:"), page); styleLayout->addWidget(styleLabel, 1, Qt::AlignLeft); } styleLayout->addSpacing(checkBoxGap); gridLayout->addLayout(styleLayout, row, 1); ++row; styleListBox = new QListWidget(page); gridLayout->addWidget(styleListBox, row, 1); // Populate usual styles, to determine minimum list width; // will be replaced later with correct styles. styleListBox->addItem(KFontChooser::tr("Normal", "QFontDatabase")); styleListBox->addItem(KFontChooser::tr("Italic", "@item font")); styleListBox->addItem(KFontChooser::tr("Oblique", "@item font")); styleListBox->addItem(KFontChooser::tr("Bold", "@item font")); styleListBox->addItem(KFontChooser::tr("Bold Italic", "@item font")); styleListBox->setMinimumWidth(minimumListWidth(styleListBox)); styleListBox->setMinimumHeight(minimumListHeight(styleListBox, visibleListSize)); connect(styleListBox, &QListWidget::currentTextChanged, [this](const QString &style) { _k_style_chosen_slot(style); }); if (flags & ShowDifferences) { styleListBox->setEnabled(false); connect(styleCheckbox, &QAbstractButton::toggled, styleListBox, &QWidget::setEnabled); } // set the the size list view / font size spinbox; and the font size checkbox row = flags & DisplayFrame ? 1 : 0; QHBoxLayout *sizeLayout = new QHBoxLayout(); if (flags & ShowDifferences) { sizeCheckbox = new QCheckBox(KFontChooser::tr("Size", "@option:check"), page); sizeLayout->addWidget(sizeCheckbox, 0, Qt::AlignLeft); sizeLabel = nullptr; } else { sizeCheckbox = nullptr; sizeLabel = new QLabel(KFontChooser::tr("Size:", "@label:listbox Font size"), page); sizeLayout->addWidget(sizeLabel, 1, Qt::AlignLeft); } sizeLayout->addSpacing(checkBoxGap); sizeLayout->addSpacing(checkBoxGap); // prevent label from eating border gridLayout->addLayout(sizeLayout, row, 2); ++row; sizeListBox = new QListWidget(page); sizeOfFont = new QDoubleSpinBox(page); sizeOfFont->setMinimum(4); sizeOfFont->setMaximum(512); sizeOfFont->setDecimals(1); sizeOfFont->setSingleStep(1); if (sizeIsRelativeState) { QString sizeIsRelativeCBText = KFontChooser::tr("Relative", "@item font size"); QString sizeIsRelativeCBToolTipText = KFontChooser::tr("Font size
fixed or relative
to environment"); QString sizeIsRelativeCBWhatsThisText = KFontChooser::tr("Here you can switch between fixed font size and font size " "to be calculated dynamically and adjusted to changing " "environment (e.g. widget dimensions, paper size)."); sizeIsRelativeCheckBox = new QCheckBox(sizeIsRelativeCBText, page); sizeIsRelativeCheckBox->setTristate(flags & ShowDifferences); QGridLayout *sizeLayout2 = new QGridLayout(); sizeLayout2->setSpacing(spacingHint / 2); gridLayout->addLayout(sizeLayout2, row, 2); sizeLayout2->setColumnStretch(1, 1); // to prevent text from eating the right border sizeLayout2->addWidget(sizeOfFont, 0, 0, 1, 2); sizeLayout2->addWidget(sizeListBox, 1, 0, 1, 2); sizeLayout2->addWidget(sizeIsRelativeCheckBox, 2, 0, Qt::AlignLeft); sizeIsRelativeCheckBox->setWhatsThis(sizeIsRelativeCBWhatsThisText); sizeIsRelativeCheckBox->setToolTip(sizeIsRelativeCBToolTipText); } else { sizeIsRelativeCheckBox = nullptr; QGridLayout *sizeLayout2 = new QGridLayout(); sizeLayout2->setSpacing(spacingHint / 2); gridLayout->addLayout(sizeLayout2, row, 2); sizeLayout2->addWidget(sizeOfFont, 0, 0); sizeLayout2->addWidget(sizeListBox, 1, 0); } // Populate with usual sizes, to determine minimum list width; // will be replaced later with correct sizes. fillSizeList(); sizeListBox->setMinimumWidth(minimumListWidth(sizeListBox) + sizeListBox->fontMetrics().maxWidth()); sizeListBox->setMinimumHeight(minimumListHeight(sizeListBox, visibleListSize)); connect(sizeOfFont, QOverload::of(&QDoubleSpinBox::valueChanged), [this](const double size) { _k_size_value_slot(size); }); connect(sizeListBox, &QListWidget::currentTextChanged, [this](const QString &size) { _k_size_chosen_slot(size); }); if (flags & ShowDifferences) { sizeListBox->setEnabled(false); sizeOfFont->setEnabled(false); connect(sizeCheckbox, &QAbstractButton::toggled, sizeListBox, &QWidget::setEnabled); connect(sizeCheckbox, &QAbstractButton::toggled, sizeOfFont, &QWidget::setEnabled); } // Add the font preview into the lower part of mainLayout sampleEdit = new QTextEdit(page); sampleEdit->setAcceptRichText(false); QFont tmpFont(q->font().family(), 64, QFont::Black); sampleEdit->setFont(tmpFont); sampleEdit->setMinimumHeight(sampleEdit->fontMetrics().lineSpacing()); // tr: A classical test phrase, with all letters of the English alphabet. // Replace it with a sample text in your language, such that it is // representative of language's writing system. // If you wish, you can input several lines of text separated by \n. q->setSampleText(KFontChooser::tr("The Quick Brown Fox Jumps Over The Lazy Dog")); sampleEdit->setTextCursor(QTextCursor(sampleEdit->document())); QString sampleEditWhatsThisText = KFontChooser::tr("This sample text illustrates the current settings. " "You may edit it to test special characters."); sampleEdit->setWhatsThis(sampleEditWhatsThisText); connect(q, &KFontChooser::fontSelected, q, [this](const QFont &font) { _k_displaySample(font); }); mainLayout->addWidget(sampleEdit); // If the calling app sets FixedFontsOnly, respect its decision - if (!usingFixed && !(flags & NoFixedCheckBox)) { + if (!usingFixed) { // Add a checkbox to toggle showing only monospace/fixed-width fonts onlyFixedCheckbox = new QCheckBox(KFontChooser::tr("Show only monospaced fonts", "@option:check")); onlyFixedCheckbox->setChecked(usingFixed); connect(onlyFixedCheckbox, &QAbstractButton::toggled, q, [this](const bool state) { q->setFont(selFont, state); }); if (flags & ShowDifferences) { // In this mode follow the state of the familyCheckbox onlyFixedCheckbox->setEnabled(false); connect(familyCheckbox, &QAbstractButton::toggled, onlyFixedCheckbox, &QWidget::setEnabled); } mainLayout->addWidget(onlyFixedCheckbox); } // Finished setting up the chooser layout // lets initialize the display if possible if (usingFixed) { q->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont), true); } else { q->setFont(QGuiApplication::font(), false); } // check or uncheck or gray out the "relative" checkbox if (sizeIsRelativeState && sizeIsRelativeCheckBox) { q->setSizeIsRelative(*sizeIsRelativeState); } // Set focus to the size list as this is the most commonly changed property sizeListBox->setFocus(); } void KFontChooser::setColor(const QColor &col) { d->m_palette.setColor(QPalette::Active, QPalette::Text, col); QPalette pal = d->sampleEdit->palette(); pal.setColor(QPalette::Active, QPalette::Text, col); d->sampleEdit->setPalette(pal); QTextCursor cursor = d->sampleEdit->textCursor(); d->sampleEdit->selectAll(); d->sampleEdit->setTextColor(col); d->sampleEdit->setTextCursor(cursor); } QColor KFontChooser::color() const { return d->m_palette.color(QPalette::Active, QPalette::Text); } void KFontChooser::setBackgroundColor(const QColor &col) { d->m_palette.setColor(QPalette::Active, QPalette::Base, col); QPalette pal = d->sampleEdit->palette(); pal.setColor(QPalette::Active, QPalette::Base, col); d->sampleEdit->setPalette(pal); } QColor KFontChooser::backgroundColor() const { return d->m_palette.color(QPalette::Active, QPalette::Base); } void KFontChooser::setSizeIsRelative(Qt::CheckState relative) { // check or uncheck or gray out the "relative" checkbox if (d->sizeIsRelativeCheckBox) { if (Qt::PartiallyChecked == relative) { d->sizeIsRelativeCheckBox->setCheckState(Qt::PartiallyChecked); } else { d->sizeIsRelativeCheckBox->setCheckState((Qt::Checked == relative) ? Qt::Checked : Qt::Unchecked); } } } Qt::CheckState KFontChooser::sizeIsRelative() const { return d->sizeIsRelativeCheckBox ? d->sizeIsRelativeCheckBox->checkState() : Qt::PartiallyChecked; } QString KFontChooser::sampleText() const { return d->sampleEdit->toPlainText(); } void KFontChooser::setSampleText(const QString &text) { d->sampleEdit->setPlainText(text); } void KFontChooser::setSampleBoxVisible(bool visible) { d->sampleEdit->setVisible(visible); } QSize KFontChooser::sizeHint(void) const { return minimumSizeHint(); } void KFontChooser::enableColumn(int column, bool state) { if (column & FamilyList) { d->familyListBox->setEnabled(state); } if (column & StyleList) { d->styleListBox->setEnabled(state); } if (column & SizeList) { d->sizeListBox->setEnabled(state); d->sizeOfFont->setEnabled(state); } } void KFontChooser::setFont(const QFont &aFont, bool onlyFixed) { d->selFont = aFont; d->selectedSize = aFont.pointSizeF(); if (d->selectedSize == -1) { d->selectedSize = QFontInfo(aFont).pointSizeF(); } if (onlyFixed != d->usingFixed) { d->usingFixed = onlyFixed; d->fillFamilyListBox(d->usingFixed); } d->setupDisplay(); } KFontChooser::FontDiffFlags KFontChooser::fontDiffFlags() const { FontDiffFlags diffFlags = NoFontDiffFlags; if (d->familyCheckbox && d->familyCheckbox->isChecked()) { diffFlags |= FontDiffFamily; } if (d->styleCheckbox && d->styleCheckbox->isChecked()) { diffFlags |= FontDiffStyle; } if (d->sizeCheckbox && d->sizeCheckbox->isChecked()) { diffFlags |= FontDiffSize; } return diffFlags; } QFont KFontChooser::font() const { return d->selFont; } void KFontChooser::Private::_k_family_chosen_slot(const QString &family) { if (!signalsAllowed) { return; } signalsAllowed = false; QString currentFamily; if (family.isEmpty()) { Q_ASSERT(familyListBox->currentItem()); if (familyListBox->currentItem()) { currentFamily = qtFamilies[familyListBox->currentItem()->text()]; } } else { currentFamily = qtFamilies[family]; } // Get the list of styles available in this family. QFontDatabase dbase; QStringList styles = dbase.styles(currentFamily); if (styles.isEmpty()) { // Avoid extraction, it is in kdeqt.po styles.append(TR_NOX("Normal", "QFontDatabase")); } // Filter style strings and add to the listbox. QString pureFamily; splitFontString(family, &pureFamily); QStringList filteredStyles; qtStyles.clear(); styleIDs.clear(); const QStringList origStyles = styles; for (const QString &style : origStyles) { // Sometimes the font database will report an invalid style, // that falls back back to another when set. // Remove such styles, by checking set/get round-trip. QFont testFont = dbase.font(currentFamily, style, 10); if (dbase.styleString(testFont) != style) { styles.removeAll(style); continue; } QString fstyle = tr("%1", "@item Font style").arg(style); if (!filteredStyles.contains(fstyle)) { filteredStyles.append(fstyle); qtStyles.insert(fstyle, style); styleIDs.insert(fstyle, styleIdentifier(testFont)); } } styleListBox->clear(); styleListBox->addItems(filteredStyles); // Try to set the current style in the listbox to that previous. int listPos = filteredStyles.indexOf(selectedStyle.isEmpty() ? TR_NOX("Normal", "QFontDatabase") : selectedStyle); if (listPos < 0) { // Make extra effort to have Italic selected when Oblique was chosen, // and vice versa, as that is what the user would probably want. QString styleIt = tr("Italic", "@item font"); QString styleOb = tr("Oblique", "@item font"); for (int i = 0; i < 2; ++i) { int pos = selectedStyle.indexOf(styleIt); if (pos >= 0) { QString style = selectedStyle; style.replace(pos, styleIt.length(), styleOb); listPos = filteredStyles.indexOf(style); if (listPos >= 0) { break; } } qSwap(styleIt, styleOb); } } styleListBox->setCurrentRow(listPos >= 0 ? listPos : 0); QString currentStyle = qtStyles[styleListBox->currentItem()->text()]; // Recompute the size listbox for this family/style. qreal currentSize = setupSizeListBox(currentFamily, currentStyle); sizeOfFont->setValue(currentSize); selFont = dbase.font(currentFamily, currentStyle, int(currentSize)); if (dbase.isSmoothlyScalable(currentFamily, currentStyle) && selFont.pointSize() == floor(currentSize)) { selFont.setPointSizeF(currentSize); } emit q->fontSelected(selFont); signalsAllowed = true; } void KFontChooser::Private::_k_style_chosen_slot(const QString &style) { if (!signalsAllowed) { return; } signalsAllowed = false; QFontDatabase dbase; QString currentFamily = qtFamilies[familyListBox->currentItem()->text()]; QString currentStyle; if (style.isEmpty()) { currentStyle = qtStyles[styleListBox->currentItem()->text()]; } else { currentStyle = qtStyles[style]; } // Recompute the size listbox for this family/style. qreal currentSize = setupSizeListBox(currentFamily, currentStyle); sizeOfFont->setValue(currentSize); selFont = dbase.font(currentFamily, currentStyle, int(currentSize)); if (dbase.isSmoothlyScalable(currentFamily, currentStyle) && selFont.pointSize() == floor(currentSize)) { selFont.setPointSizeF(currentSize); } emit q->fontSelected(selFont); if (!style.isEmpty()) { selectedStyle = currentStyle; } signalsAllowed = true; } void KFontChooser::Private::_k_size_chosen_slot(const QString &size) { if (!signalsAllowed) { return; } signalsAllowed = false; qreal currentSize; if (size.isEmpty()) { currentSize = QLocale::system().toDouble(sizeListBox->currentItem()->text()); } else { currentSize = QLocale::system().toDouble(size); } // Reset the customized size slot in the list if not needed. if (customSizeRow >= 0 && selFont.pointSizeF() != currentSize) { sizeListBox->item(customSizeRow)->setText(standardSizeAtCustom); customSizeRow = -1; } sizeOfFont->setValue(currentSize); selFont.setPointSizeF(currentSize); emit q->fontSelected(selFont); if (!size.isEmpty()) { selectedSize = currentSize; } signalsAllowed = true; } void KFontChooser::Private::_k_size_value_slot(double dval) { if (!signalsAllowed) { return; } signalsAllowed = false; // We compare with qreal, so convert for platforms where qreal != double. qreal val = qreal(dval); QFontDatabase dbase; QString family = qtFamilies[familyListBox->currentItem()->text()]; QString style = qtStyles[styleListBox->currentItem()->text()]; // Reset current size slot in list if it was customized. if (customSizeRow >= 0 && sizeListBox->currentRow() == customSizeRow) { sizeListBox->item(customSizeRow)->setText(standardSizeAtCustom); customSizeRow = -1; } bool canCustomize = true; // For Qt-bad-sizes workaround: skip this block unconditionally if (!dbase.isSmoothlyScalable(family, style)) { // Bitmap font, allow only discrete sizes. // Determine the nearest in the direction of change. canCustomize = false; int nrows = sizeListBox->count(); int row = sizeListBox->currentRow(); int nrow; if (val - selFont.pointSizeF() > 0) { for (nrow = row + 1; nrow < nrows; ++nrow) if (QLocale::system().toDouble(sizeListBox->item(nrow)->text()) >= val) { break; } } else { for (nrow = row - 1; nrow >= 0; --nrow) if (QLocale::system().toDouble(sizeListBox->item(nrow)->text()) <= val) { break; } } // Make sure the new row is not out of bounds. nrow = nrow < 0 ? 0 : nrow >= nrows ? nrows - 1 : nrow; // Get the size from the new row and set the spinbox to that size. val = QLocale::system().toDouble(sizeListBox->item(nrow)->text()); sizeOfFont->setValue(val); } // Set the current size in the size listbox. int row = nearestSizeRow(val, canCustomize); sizeListBox->setCurrentRow(row); selectedSize = val; selFont.setPointSizeF(val); emit q->fontSelected(selFont); signalsAllowed = true; } void KFontChooser::Private::_k_displaySample(const QFont &font) { sampleEdit->setFont(font); //sampleEdit->setCursorPosition(0); //QFontInfo a = QFontInfo(font); //qCDebug(KWidgetsAddonsLog) << "font: " << a.family () << ", " << a.pointSize (); //qCDebug(KWidgetsAddonsLog) << " (" << font.toString() << ")\n"; } int KFontChooser::Private::nearestSizeRow(qreal val, bool customize) { qreal diff = 1000; int row = 0; for (int r = 0; r < sizeListBox->count(); ++r) { qreal cval = QLocale::system().toDouble(sizeListBox->item(r)->text()); if (qAbs(cval - val) < diff) { diff = qAbs(cval - val); row = r; } } // For Qt-bad-sizes workaround: ignore value of customize, use true if (customize && diff > 0) { customSizeRow = row; standardSizeAtCustom = sizeListBox->item(row)->text(); sizeListBox->item(row)->setText(formatFontSize(val)); } return row; } qreal KFontChooser::Private::fillSizeList(const QList &sizes_) { if (!sizeListBox) { return 0; //assertion. } QList sizes = sizes_; bool canCustomize = false; if (sizes.isEmpty()) { 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 }; for (int i = 0; c[i]; ++i) { sizes.append(c[i]); } // Since sizes were not supplied, this is a vector font, // and size slot customization is allowed. canCustomize = true; } // Insert sizes into the listbox. sizeListBox->clear(); std::sort(sizes.begin(), sizes.end()); for (qreal size : qAsConst(sizes)) { sizeListBox->addItem(formatFontSize(size)); } // Return the nearest to selected size. // If the font is vector, the nearest size is always same as selected, // thus size slot customization is allowed. // If the font is bitmap, the nearest size need not be same as selected, // thus size slot customization is not allowed. customSizeRow = -1; int row = nearestSizeRow(selectedSize, canCustomize); return QLocale::system().toDouble(sizeListBox->item(row)->text()); } qreal KFontChooser::Private::setupSizeListBox(const QString &family, const QString &style) { QFontDatabase dbase; QList sizes; const bool smoothlyScalable = dbase.isSmoothlyScalable(family, style); if (!smoothlyScalable) { const QList smoothSizes = dbase.smoothSizes(family, style); for (int size : smoothSizes) { sizes.append(size); } } // Fill the listbox (uses default list of sizes if the given is empty). // Collect the best fitting size to selected size, to use if not smooth. qreal bestFitSize = fillSizeList(sizes); // Set the best fit size as current in the listbox if available. const QList selectedSizeList = sizeListBox->findItems( formatFontSize(bestFitSize), Qt::MatchExactly); if (!selectedSizeList.isEmpty()) { sizeListBox->setCurrentItem(selectedSizeList.first()); } return bestFitSize; } void KFontChooser::Private::setupDisplay() { QFontDatabase dbase; QString family = selFont.family().toLower(); QString styleID = styleIdentifier(selFont); qreal size = selFont.pointSizeF(); if (size == -1) { size = QFontInfo(selFont).pointSizeF(); } int numEntries, i; // Direct family match. numEntries = familyListBox->count(); for (i = 0; i < numEntries; ++i) { if (family == qtFamilies[familyListBox->item(i)->text()].toLower()) { familyListBox->setCurrentRow(i); break; } } // 1st family fallback. if (i == numEntries) { const int bracketPos = family.indexOf(QLatin1Char('[')); if (bracketPos != -1) { family = family.leftRef(bracketPos).trimmed().toString(); for (i = 0; i < numEntries; ++i) { if (family == qtFamilies[familyListBox->item(i)->text()].toLower()) { familyListBox->setCurrentRow(i); break; } } } } // 2nd family fallback. if (i == numEntries) { QString fallback = family + QLatin1String(" ["); for (i = 0; i < numEntries; ++i) { if (qtFamilies[familyListBox->item(i)->text()].toLower().startsWith(fallback)) { familyListBox->setCurrentRow(i); break; } } } // 3rd family fallback. if (i == numEntries) { for (i = 0; i < numEntries; ++i) { if (qtFamilies[familyListBox->item(i)->text()].toLower().startsWith(family)) { familyListBox->setCurrentRow(i); break; } } } // Family fallback in case nothing matched. Otherwise, diff doesn't work if (i == numEntries) { familyListBox->setCurrentRow(0); } // By setting the current item in the family box, the available // styles and sizes for that family have been collected. // Try now to set the current items in the style and size boxes. // Set current style in the listbox. numEntries = styleListBox->count(); for (i = 0; i < numEntries; ++i) { if (styleID == styleIDs[styleListBox->item(i)->text()]) { styleListBox->setCurrentRow(i); break; } } if (i == numEntries) { // Style not found, fallback. styleListBox->setCurrentRow(0); } // Set current size in the listbox. // If smoothly scalable, allow customizing one of the standard size slots, // otherwise just select the nearest available size. QString currentFamily = qtFamilies[familyListBox->currentItem()->text()]; QString currentStyle = qtStyles[styleListBox->currentItem()->text()]; bool canCustomize = dbase.isSmoothlyScalable(currentFamily, currentStyle); sizeListBox->setCurrentRow(nearestSizeRow(size, canCustomize)); // Set current size in the spinbox. sizeOfFont->setValue(QLocale::system().toDouble(sizeListBox->currentItem()->text())); } void KFontChooser::getFontList(QStringList &list, uint fontListCriteria) { QFontDatabase dbase; QStringList lstSys(dbase.families()); // if we have criteria; then check fonts before adding if (fontListCriteria) { QStringList lstFonts; for (const QString &family : qAsConst(lstSys)) { if ((fontListCriteria & FixedWidthFonts) > 0 && !dbase.isFixedPitch(family)) { continue; } if (((fontListCriteria & (SmoothScalableFonts | ScalableFonts)) == ScalableFonts) && !dbase.isBitmapScalable(family)) { continue; } if ((fontListCriteria & SmoothScalableFonts) > 0 && !dbase.isSmoothlyScalable(family)) { continue; } lstFonts.append(family); } if ((fontListCriteria & FixedWidthFonts) > 0) { // Fallback.. if there are no fixed fonts found, it's probably a // bug in the font server or Qt. In this case, just use 'fixed' if (lstFonts.isEmpty()) { lstFonts.append(QStringLiteral("fixed")); } } lstSys = lstFonts; } lstSys.sort(); list = lstSys; } void KFontChooser::Private::setFamilyBoxItems(const QStringList &fonts) { signalsAllowed = false; QStringList trfonts = translateFontNameList(fonts, &qtFamilies); familyListBox->clear(); familyListBox->addItems(trfonts); signalsAllowed = true; } void KFontChooser::Private::fillFamilyListBox(bool onlyFixedFonts) { QStringList fontList; getFontList(fontList, onlyFixedFonts ? FixedWidthFonts : 0); setFamilyBoxItems(fontList); } // Human-readable style identifiers returned by QFontDatabase::styleString() // do not always survive round trip of QFont serialization/deserialization, // causing wrong style in the style box to be highlighted when // the chooser dialog is opened. This will cause the style to be changed // when the dialog is closed and the user did not touch the style box. // Hence, construct custom style identifiers sufficient for the purpose. QString KFontChooser::Private::styleIdentifier(const QFont &font) { const int weight = font.weight(); QString styleName = font.styleName(); // If the styleName property is empty and the weight is QFont::Normal, that // could mean it's a "Regular"-like style with the styleName part stripped // so that subsequent calls to setBold(true) can work properly (i.e. selecting // the "Bold" style provided by the font itself) without resorting to font // "emboldening" which looks ugly. // See also KConfigGroupGui::writeEntryGui(). if (styleName.isEmpty() && weight == QFont::Normal) { QFontDatabase fdb; const QStringList styles = fdb.styles(font.family()); for (const QString &style : styles) { // orderded by commonness, i.e. "Regular" is the most common if (style == QLatin1String("Regular") || style == QLatin1String("Normal") || style == QLatin1String("Book") || style == QLatin1String("Roman")) { styleName = style; } else { // nothing more we can do } } } const QChar comma(QLatin1Char(',')); return QString::number(weight) + comma + QString::number((int)font.style()) + comma + QString::number(font.stretch()) + comma + styleName; } #include "moc_kfontchooser.cpp" diff --git a/src/kfontchooser.h b/src/kfontchooser.h index 5c87118..6c88cbe 100644 --- a/src/kfontchooser.h +++ b/src/kfontchooser.h @@ -1,289 +1,288 @@ /* Copyright (C) 1997 Bernd Johannes Wuebben Copyright (c) 1999 Preston Brown Copyright (c) 1999 Mario Weilguni This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef K_FONT_CHOOSER_H #define K_FONT_CHOOSER_H #include #include class QFont; class QStringList; /** * @class KFontChooser kfontchooser.h KFontChooser * * @short A font selection widget. * * While KFontChooser as an ordinary widget can be embedded in * custom dialogs and therefore is very flexible, in most cases * it is preferable to use the convenience functions in * QFontDialog. * * \image html kfontchooser.png "KFontChooser Widget" * * @see KFontRequester * * @author Preston Brown , Bernd Wuebben */ class KWIDGETSADDONS_EXPORT KFontChooser : public QWidget { Q_OBJECT Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontSelected USER true) Q_PROPERTY(QColor color READ color WRITE setColor) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) Q_PROPERTY(Qt::CheckState sizeIsRelative READ sizeIsRelative WRITE setSizeIsRelative) Q_PROPERTY(QString sampleText READ sampleText WRITE setSampleText) public: /** * Displayed columns. */ enum FontColumn { FamilyList = 0x01, ///< Identifies the family (leftmost) list. StyleList = 0x02, ///< Identifies the style (center) list. SizeList = 0x04 ///< Identifies the size (rightmost) list. }; /** * Flags for selecting which font attributes to change * @see FontDiffFlags */ enum FontDiff { NoFontDiffFlags = 0, ///< No flags set FontDiffFamily = 1, ///< Identifies a requested change in the font family. FontDiffStyle = 2, ///< Identifies a requested change in the font style. FontDiffSize = 4, ///< Identifies a requested change in the font size. AllFontDiffs = FontDiffFamily | FontDiffStyle | FontDiffSize }; /** * Stores an combination of #FontDiff values. */ Q_DECLARE_FLAGS(FontDiffFlags, FontDiff) /** * Flags for selecting what is displayed in the widget. * @see DisplayFlags */ enum DisplayFlag { NoDisplayFlags = 0, ///< No flags set FixedFontsOnly = 1, ///< Only show monospaced/fixed-width fonts, excluding proportional fonts, (the ///< checkbox to toggle showing only monospaced fonts is not shown in this case) DisplayFrame = 2, ///< Show a visual frame around the chooser ShowDifferences = 4, ///< Display the font differences interfaces - NoFixedCheckBox = 8, ///< Do not Show a checkbox to toggle showing only fixed fonts }; /** * Stores a combination of #DisplayFlag values. */ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag) /** * Constructs a font picker widget. * It normally comes up with all font families present on the system; the * getFont method below does allow some more fine-tuning of the selection of fonts * that will be displayed in the dialog. *

Consider the following code snippet; * \code * QStringList list; * KFontChooser::getFontList(list, KFontChooser::SmoothScalableFonts); * KFontChooser *chooseFont = new KFontChooser(nullptr, KFontChooser::NoDisplayFlags, list); * \endcode *

* The above creates a font chooser dialog with only SmoothScaleble fonts. * * @param parent The parent widget. * @param flags Defines how the font chooser is displayed. * @param fontList A list of fonts to display, in XLFD format. If * no list is formatted, the internal KDE font list is used. * If that has not been created, X is queried, and all fonts * available on the system are displayed. * @param visibleListSize The minimum number of visible entries in the * fontlists. * @param sizeIsRelativeState If not zero the widget will show a * checkbox where the user may choose whether the font size * is to be interpreted as relative size. * Initial state of this checkbox will be set according to * *sizeIsRelativeState, user choice may be retrieved by * calling sizeIsRelative(). */ explicit KFontChooser(QWidget *parent = nullptr, const DisplayFlags &flags = DisplayFrame, const QStringList &fontList = QStringList(), int visibleListSize = 8, Qt::CheckState *sizeIsRelativeState = nullptr); /** * Destructs the font chooser. */ ~KFontChooser() override; /** * Enables or disable a font column in the chooser. * * Use this * function if your application does not need or supports all font * properties. * * @param column Specify the columns. An or'ed combination of * @p FamilyList, @p StyleList and @p SizeList is possible. * @param state If @p false the columns are disabled. */ void enableColumn(int column, bool state); /** * Sets the currently selected font in the chooser. * * @param font The font to select. * @param onlyFixed Readjust the font list to display only fixed * width fonts if @p true, or vice-versa. */ void setFont(const QFont &font, bool onlyFixed = false); /** * @return The bitmask corresponding to the attributes the user * wishes to change. */ FontDiffFlags fontDiffFlags() const; /** * @return The currently selected font in the chooser. */ QFont font() const; /** * Sets the color to use in the preview. */ void setColor(const QColor &col); /** * @return The color currently used in the preview (default: the text * color of the active color group) */ QColor color() const; /** * Sets the background color to use in the preview. */ void setBackgroundColor(const QColor &col); /** * @return The background color currently used in the preview (default: * the base color of the active colorgroup) */ QColor backgroundColor() const; /** * Sets the state of the checkbox indicating whether the font size * is to be interpreted as relative size. * NOTE: If parameter sizeIsRelative was not set in the constructor * of the widget this setting will be ignored. */ void setSizeIsRelative(Qt::CheckState relative); /** * @return Whether the font size is to be interpreted as relative size * (default: QButton:Off) */ Qt::CheckState sizeIsRelative() const; /** * @return The current text in the sample text input area. */ QString sampleText() const; /** * Sets the sample text. * * Normally you should not change this * text, but it can be better to do this if the default text is * too large for the edit area when using the default font of your * application. * * @param text The new sample text. The current will be removed. */ void setSampleText(const QString &text); /** * Shows or hides the sample text box. * * @param visible Set it to true to show the box, to false to hide it. */ void setSampleBoxVisible(bool visible); /** * The selection criteria for the font families shown in the dialog. */ enum FontListCriteria { /** * When included only fixed-width fonts are returned. * The fonts where the width of every character is equal. */ FixedWidthFonts = 0x01, /** * When included only scalable fonts are returned; * certain configurations allow bitmap fonts to remain unscaled and * thus these fonts have limited number of sizes. */ ScalableFonts = 0x02, /** * When included only return smooth scalable fonts. * This will return only non-bitmap fonts which are scalable to any size requested. * Setting this option to true will mean the "scalable" flag is irrelevant. */ SmoothScalableFonts = 0x04 }; /** * Creates a list of font strings. * * @param list The list is returned here. * @param fontListCriteria should contain all the restrictions for font selection as OR-ed values * from KFontChooser::FontListCriteria */ static void getFontList(QStringList &list, uint fontListCriteria); /** * Reimplemented for internal reasons. */ QSize sizeHint(void) const override; Q_SIGNALS: /** * Emitted whenever the selected font changes. */ void fontSelected(const QFont &font); private: class Private; Private *const d; Q_DISABLE_COPY(KFontChooser) Q_PRIVATE_SLOT(d, void _k_family_chosen_slot(const QString &)) Q_PRIVATE_SLOT(d, void _k_size_chosen_slot(const QString &)) Q_PRIVATE_SLOT(d, void _k_style_chosen_slot(const QString &)) Q_PRIVATE_SLOT(d, void _k_displaySample(const QFont &font)) Q_PRIVATE_SLOT(d, void _k_size_value_slot(double)) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KFontChooser::DisplayFlags) #endif diff --git a/tests/kfontchooserdialogtest.cpp b/tests/kfontchooserdialogtest.cpp index 0beaf01..2e1253e 100644 --- a/tests/kfontchooserdialogtest.cpp +++ b/tests/kfontchooserdialogtest.cpp @@ -1,63 +1,55 @@ /* Copyright (C) 1996 Bernd Johannes Wuebben wuebben@math.cornell.edu This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfontchooserdialog.h" #include #include int main(int argc, char **argv) { QApplication::setApplicationName(QStringLiteral("KFontChooserDialogTest")); QApplication app(argc, argv); app.setFont(QFont(QStringLiteral("Noto Sans"), 11)); // QFont font = QFont("Times",18,QFont::Bold); QFont font; qDebug() << "Default use case, all bells and whistles"; int nRet = KFontChooserDialog::getFont(font); qDebug() << font.toString(); qDebug() << "Only show monospaced fonts, FixedOnly checkbox is _not_ shown"; nRet = KFontChooserDialog::getFont(font, KFontChooser::FixedFontsOnly); qDebug() << font.toString(); - qDebug() << "Don't show the FixedOnly checkbox"; - nRet = KFontChooserDialog::getFont(font, KFontChooser::NoFixedCheckBox); - qDebug() << font.toString(); - KFontChooser::FontDiffFlags diffFlags; qDebug() << "ShowDifferences mode"; nRet = KFontChooserDialog::getFontDiff(font, diffFlags); qDebug() << font.toString(); qDebug() << "ShowDifferences mode and only showing monospaced fonts (the FixedOnly checkbox is _not_ shown)"; nRet = KFontChooserDialog::getFontDiff(font, diffFlags, KFontChooser::FixedFontsOnly); qDebug() << font.toString(); - qDebug() << "ShowDifferences mode with FixeOnly checkbox not shown"; - nRet = KFontChooserDialog::getFontDiff(font, diffFlags, KFontChooser::NoFixedCheckBox); - qDebug() << font.toString(); - return nRet; }