| 1 | // Copyright (C) 2016 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 3 |  | 
| 4 | #include "fontpanel_p.h" | 
| 5 |  | 
| 6 | #include <QtWidgets/QLabel> | 
| 7 | #include <QtWidgets/QComboBox> | 
| 8 | #include <QtWidgets/QFormLayout> | 
| 9 | #include <QtWidgets/QSpacerItem> | 
| 10 | #include <QtWidgets/QFontComboBox> | 
| 11 | #include <QtCore/QTimer> | 
| 12 | #include <QtWidgets/QLineEdit> | 
| 13 |  | 
| 14 | QT_BEGIN_NAMESPACE | 
| 15 |  | 
| 16 | using namespace Qt::StringLiterals; | 
| 17 |  | 
| 18 | FontPanel::FontPanel(QWidget *parentWidget) : | 
| 19 |     QGroupBox(parentWidget), | 
| 20 |     m_previewLineEdit(new QLineEdit), | 
| 21 |     m_writingSystemComboBox(new QComboBox), | 
| 22 |     m_familyComboBox(new QFontComboBox), | 
| 23 |     m_styleComboBox(new QComboBox), | 
| 24 |     m_pointSizeComboBox(new QComboBox), | 
| 25 |     m_previewFontUpdateTimer(0) | 
| 26 | { | 
| 27 |     setTitle(tr(s: "Font" )); | 
| 28 |  | 
| 29 |     QFormLayout *formLayout = new QFormLayout(this); | 
| 30 |     // writing systems | 
| 31 |     m_writingSystemComboBox->setEditable(false); | 
| 32 |  | 
| 33 |     auto writingSystems = QFontDatabase::writingSystems(); | 
| 34 |     writingSystems.push_front(t: QFontDatabase::Any); | 
| 35 |     for (QFontDatabase::WritingSystem ws : std::as_const(t&: writingSystems)) | 
| 36 |         m_writingSystemComboBox->addItem(atext: QFontDatabase::writingSystemName(writingSystem: ws), auserData: QVariant(ws)); | 
| 37 |     connect(sender: m_writingSystemComboBox, signal: &QComboBox::currentIndexChanged, | 
| 38 |             context: this, slot: &FontPanel::slotWritingSystemChanged); | 
| 39 |     formLayout->addRow(labelText: tr(s: "&Writing system" ), field: m_writingSystemComboBox); | 
| 40 |  | 
| 41 |     connect(sender: m_familyComboBox, signal: &QFontComboBox::currentFontChanged, | 
| 42 |             context: this, slot: &FontPanel::slotFamilyChanged); | 
| 43 |     formLayout->addRow(labelText: tr(s: "&Family" ), field: m_familyComboBox); | 
| 44 |  | 
| 45 |     m_styleComboBox->setEditable(false); | 
| 46 |     connect(sender: m_styleComboBox, signal: &QComboBox::currentIndexChanged, | 
| 47 |             context: this, slot: &FontPanel::slotStyleChanged); | 
| 48 |     formLayout->addRow(labelText: tr(s: "&Style" ), field: m_styleComboBox); | 
| 49 |  | 
| 50 |     m_pointSizeComboBox->setEditable(false); | 
| 51 |     connect(sender: m_pointSizeComboBox, signal: &QComboBox::currentIndexChanged, | 
| 52 |             context: this, slot: &FontPanel::slotPointSizeChanged); | 
| 53 |     formLayout->addRow(labelText: tr(s: "&Point size" ), field: m_pointSizeComboBox); | 
| 54 |  | 
| 55 |     m_previewLineEdit->setReadOnly(true); | 
| 56 |     formLayout->addRow(widget: m_previewLineEdit); | 
| 57 |  | 
| 58 |     setWritingSystem(QFontDatabase::Any); | 
| 59 | } | 
| 60 |  | 
| 61 | QFont FontPanel::selectedFont() const | 
| 62 | { | 
| 63 |     QFont rc = m_familyComboBox->currentFont(); | 
| 64 |     const QString family = rc.family(); | 
| 65 |     rc.setPointSize(pointSize()); | 
| 66 |     const QString styleDescription = styleString(); | 
| 67 |     if (styleDescription.contains(s: "Italic"_L1 )) | 
| 68 |         rc.setStyle(QFont::StyleItalic); | 
| 69 |     else if (styleDescription.contains(s: "Oblique"_L1 )) | 
| 70 |         rc.setStyle(QFont::StyleOblique); | 
| 71 |     else | 
| 72 |         rc.setStyle(QFont::StyleNormal); | 
| 73 |     rc.setBold(QFontDatabase::bold(family, style: styleDescription)); | 
| 74 |     rc.setWeight(QFont::Weight(QFontDatabase::weight(family, style: styleDescription))); | 
| 75 |     return rc; | 
| 76 | } | 
| 77 |  | 
| 78 | void FontPanel::setSelectedFont(const QFont &f) | 
| 79 | { | 
| 80 |     m_familyComboBox->setCurrentFont(f); | 
| 81 |     if (m_familyComboBox->currentIndex() < 0) { | 
| 82 |         // family not in writing system - find the corresponding one? | 
| 83 |         QList<QFontDatabase::WritingSystem> familyWritingSystems = QFontDatabase::writingSystems(family: f.family()); | 
| 84 |         if (familyWritingSystems.isEmpty()) | 
| 85 |             return; | 
| 86 |  | 
| 87 |         setWritingSystem(familyWritingSystems.constFirst()); | 
| 88 |         m_familyComboBox->setCurrentFont(f); | 
| 89 |     } | 
| 90 |  | 
| 91 |     updateFamily(family: family()); | 
| 92 |  | 
| 93 |     const int pointSizeIndex = closestPointSizeIndex(ps: f.pointSize()); | 
| 94 |     m_pointSizeComboBox->setCurrentIndex( pointSizeIndex); | 
| 95 |  | 
| 96 |     const QString styleString = QFontDatabase::styleString(font: f); | 
| 97 |     const int styleIndex = m_styleComboBox->findText(text: styleString); | 
| 98 |     m_styleComboBox->setCurrentIndex(styleIndex); | 
| 99 |     slotUpdatePreviewFont(); | 
| 100 | } | 
| 101 |  | 
| 102 |  | 
| 103 | QFontDatabase::WritingSystem FontPanel::writingSystem() const | 
| 104 | { | 
| 105 |     const int currentIndex = m_writingSystemComboBox->currentIndex(); | 
| 106 |     if ( currentIndex == -1) | 
| 107 |         return QFontDatabase::Latin; | 
| 108 |     return static_cast<QFontDatabase::WritingSystem>(m_writingSystemComboBox->itemData(index: currentIndex).toInt()); | 
| 109 | } | 
| 110 |  | 
| 111 | QString FontPanel::family() const | 
| 112 | { | 
| 113 |     const int currentIndex = m_familyComboBox->currentIndex(); | 
| 114 |     return currentIndex != -1 ?  m_familyComboBox->currentFont().family() : QString(); | 
| 115 | } | 
| 116 |  | 
| 117 | int FontPanel::pointSize() const | 
| 118 | { | 
| 119 |     const int currentIndex = m_pointSizeComboBox->currentIndex(); | 
| 120 |     return currentIndex != -1 ? m_pointSizeComboBox->itemData(index: currentIndex).toInt() : 9; | 
| 121 | } | 
| 122 |  | 
| 123 | QString FontPanel::styleString() const | 
| 124 | { | 
| 125 |     const int currentIndex = m_styleComboBox->currentIndex(); | 
| 126 |     return currentIndex != -1 ? m_styleComboBox->itemText(index: currentIndex) : QString(); | 
| 127 | } | 
| 128 |  | 
| 129 | void FontPanel::setWritingSystem(QFontDatabase::WritingSystem ws) | 
| 130 | { | 
| 131 |     m_writingSystemComboBox->setCurrentIndex(m_writingSystemComboBox->findData(data: QVariant(ws))); | 
| 132 |     updateWritingSystem(ws); | 
| 133 | } | 
| 134 |  | 
| 135 |  | 
| 136 | void FontPanel::slotWritingSystemChanged(int) | 
| 137 | { | 
| 138 |     updateWritingSystem(ws: writingSystem()); | 
| 139 |     delayedPreviewFontUpdate(); | 
| 140 | } | 
| 141 |  | 
| 142 | void FontPanel::slotFamilyChanged(const QFont &) | 
| 143 | { | 
| 144 |     updateFamily(family: family()); | 
| 145 |     delayedPreviewFontUpdate(); | 
| 146 | } | 
| 147 |  | 
| 148 | void FontPanel::slotStyleChanged(int) | 
| 149 | { | 
| 150 |     updatePointSizes(family: family(), style: styleString()); | 
| 151 |     delayedPreviewFontUpdate(); | 
| 152 | } | 
| 153 |  | 
| 154 | void FontPanel::slotPointSizeChanged(int) | 
| 155 | { | 
| 156 |     delayedPreviewFontUpdate(); | 
| 157 | } | 
| 158 |  | 
| 159 | void FontPanel::updateWritingSystem(QFontDatabase::WritingSystem ws) | 
| 160 | { | 
| 161 |  | 
| 162 |     m_previewLineEdit->setText(QFontDatabase::writingSystemSample(writingSystem: ws)); | 
| 163 |     m_familyComboBox->setWritingSystem (ws); | 
| 164 |     // Current font not in WS ... set index 0. | 
| 165 |     if (m_familyComboBox->currentIndex() < 0) { | 
| 166 |         m_familyComboBox->setCurrentIndex(0); | 
| 167 |         updateFamily(family: family()); | 
| 168 |     } | 
| 169 | } | 
| 170 |  | 
| 171 | void FontPanel::updateFamily(const QString &family) | 
| 172 | { | 
| 173 |     // Update styles and trigger update of point sizes. | 
| 174 |     // Try to maintain selection or select normal | 
| 175 |     const QString &oldStyleString = styleString(); | 
| 176 |  | 
| 177 |     const QStringList &styles = QFontDatabase::styles(family); | 
| 178 |     const bool hasStyles = !styles.isEmpty(); | 
| 179 |  | 
| 180 |     m_styleComboBox->setCurrentIndex(-1); | 
| 181 |     m_styleComboBox->clear(); | 
| 182 |     m_styleComboBox->setEnabled(hasStyles); | 
| 183 |  | 
| 184 |     int normalIndex = -1; | 
| 185 |     const QString normalStyle = "Normal"_L1 ; | 
| 186 |  | 
| 187 |     if (hasStyles) { | 
| 188 |         for (const QString &style : styles) { | 
| 189 |             // try to maintain selection or select 'normal' preferably | 
| 190 |             const int newIndex = m_styleComboBox->count(); | 
| 191 |             m_styleComboBox->addItem(atext: style); | 
| 192 |             if (oldStyleString == style) { | 
| 193 |                 m_styleComboBox->setCurrentIndex(newIndex); | 
| 194 |             } else { | 
| 195 |                 if (oldStyleString ==  normalStyle) | 
| 196 |                     normalIndex = newIndex; | 
| 197 |             } | 
| 198 |         } | 
| 199 |         if (m_styleComboBox->currentIndex() == -1 && normalIndex != -1) | 
| 200 |             m_styleComboBox->setCurrentIndex(normalIndex); | 
| 201 |     } | 
| 202 |     updatePointSizes(family, style: styleString()); | 
| 203 | } | 
| 204 |  | 
| 205 | int FontPanel::closestPointSizeIndex(int desiredPointSize) const | 
| 206 | { | 
| 207 |     //  try to maintain selection or select closest. | 
| 208 |     int closestIndex = -1; | 
| 209 |     int closestAbsError = 0xFFFF; | 
| 210 |  | 
| 211 |     const int pointSizeCount = m_pointSizeComboBox->count(); | 
| 212 |     for (int i = 0; i < pointSizeCount; i++) { | 
| 213 |         const int itemPointSize = m_pointSizeComboBox->itemData(index: i).toInt(); | 
| 214 |         const int absError = qAbs(t: desiredPointSize - itemPointSize); | 
| 215 |         if (absError < closestAbsError) { | 
| 216 |             closestIndex  = i; | 
| 217 |             closestAbsError = absError; | 
| 218 |             if (closestAbsError == 0) | 
| 219 |                 break; | 
| 220 |         } else {    // past optimum | 
| 221 |             if (absError > closestAbsError) { | 
| 222 |                 break; | 
| 223 |             } | 
| 224 |         } | 
| 225 |     } | 
| 226 |     return closestIndex; | 
| 227 | } | 
| 228 |  | 
| 229 |  | 
| 230 | void FontPanel::updatePointSizes(const QString &family, const QString &styleString) | 
| 231 | { | 
| 232 |     const int oldPointSize = pointSize(); | 
| 233 |  | 
| 234 |     auto pointSizes =  QFontDatabase::pointSizes(family, style: styleString); | 
| 235 |     if (pointSizes.isEmpty()) | 
| 236 |         pointSizes = QFontDatabase::standardSizes(); | 
| 237 |  | 
| 238 |     const bool hasSizes = !pointSizes.isEmpty(); | 
| 239 |     m_pointSizeComboBox->clear(); | 
| 240 |     m_pointSizeComboBox->setEnabled(hasSizes); | 
| 241 |     m_pointSizeComboBox->setCurrentIndex(-1); | 
| 242 |  | 
| 243 |     //  try to maintain selection or select closest. | 
| 244 |     if (hasSizes) { | 
| 245 |         QString n; | 
| 246 |         for (int pointSize : std::as_const(t&: pointSizes)) | 
| 247 |             m_pointSizeComboBox->addItem(atext: n.setNum(n: pointSize), auserData: QVariant(pointSize)); | 
| 248 |         const int closestIndex = closestPointSizeIndex(desiredPointSize: oldPointSize); | 
| 249 |         if (closestIndex != -1) | 
| 250 |             m_pointSizeComboBox->setCurrentIndex(closestIndex); | 
| 251 |     } | 
| 252 | } | 
| 253 |  | 
| 254 | void FontPanel::slotUpdatePreviewFont() | 
| 255 | { | 
| 256 |     m_previewLineEdit->setFont(selectedFont()); | 
| 257 | } | 
| 258 |  | 
| 259 | void FontPanel::delayedPreviewFontUpdate() | 
| 260 | { | 
| 261 |     if (!m_previewFontUpdateTimer) { | 
| 262 |         m_previewFontUpdateTimer = new QTimer(this); | 
| 263 |         connect(sender: m_previewFontUpdateTimer, signal: &QTimer::timeout, | 
| 264 |                 context: this, slot: &FontPanel::slotUpdatePreviewFont); | 
| 265 |         m_previewFontUpdateTimer->setInterval(0); | 
| 266 |         m_previewFontUpdateTimer->setSingleShot(true); | 
| 267 |     } | 
| 268 |     if (m_previewFontUpdateTimer->isActive()) | 
| 269 |         return; | 
| 270 |     m_previewFontUpdateTimer->start(); | 
| 271 | } | 
| 272 |  | 
| 273 | QT_END_NAMESPACE | 
| 274 |  |