File indexing completed on 2024-05-12 15:42:39
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Jonah BrĂ¼chert <jbb@kaidan.im> 0003 * SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "units.h" 0009 0010 #include <QFont> 0011 #include <QFontMetrics> 0012 #include <QGuiApplication> 0013 #include <QQmlComponent> 0014 #include <QQmlEngine> 0015 #include <QStyleHints> 0016 0017 #include <chrono> 0018 #include <cmath> 0019 0020 #include "loggingcategory.h" 0021 0022 namespace Kirigami { 0023 using clock = std::chrono::steady_clock; 0024 0025 const clock::duration rateLimit = std::chrono::seconds(1); 0026 0027 /* Print a deprecation warning that is rate limited to only display once in 0028 * every time period as determined by rateLimit. We keep track of how often this 0029 * is called and display that if it is larger than 0. 0030 * 0031 * This is done to prevent flooding the logs with "X is deprecated" messages 0032 * that are all the same and don't provide any new information after the first. 0033 */ 0034 void rateLimitWarning(const char *method, const char *since, const char *message) 0035 { 0036 static QMap<QString, QPair<clock::time_point, int>> messages; 0037 0038 auto methodString = QString::fromUtf8(method); 0039 0040 if (!messages.contains(methodString)) { 0041 messages.insert(methodString, qMakePair(clock::time_point{}, 0)); 0042 } 0043 0044 auto entry = messages.value(methodString); 0045 if (clock::now() - entry.first < rateLimit) { 0046 messages[methodString].second += 1; 0047 return; 0048 } 0049 0050 qCWarning(KirigamiLog).nospace() << method << " is deprecated (since " << since << "): " << message; 0051 0052 if (entry.second > 0) { 0053 qCWarning(KirigamiLog) << "Previous message repeats" << entry.second << "times."; 0054 } 0055 0056 messages[methodString] = qMakePair(clock::now(), 0); 0057 } 0058 0059 class UnitsPrivate 0060 { 0061 Q_DISABLE_COPY(UnitsPrivate) 0062 0063 public: 0064 explicit UnitsPrivate(Units *units) 0065 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0066 : qmlFontMetrics(nullptr) 0067 #endif 0068 // Cache font so we don't have to go through QVariant and property every time 0069 , fontMetrics(QFontMetricsF(QGuiApplication::font())) 0070 , gridUnit(fontMetrics.height()) 0071 , smallSpacing(std::floor(gridUnit / 4)) 0072 , mediumSpacing(std::round(smallSpacing * 1.5)) 0073 , largeSpacing(smallSpacing * 2) 0074 , veryLongDuration(400) 0075 , longDuration(200) 0076 , shortDuration(100) 0077 , veryShortDuration(50) 0078 , humanMoment(2000) 0079 , toolTipDelay(700) 0080 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0081 , wheelScrollLines(QGuiApplication::styleHints()->wheelScrollLines()) 0082 #endif 0083 , iconSizes(new IconSizes(units)) 0084 { 0085 } 0086 0087 // Only stored for QML API compatiblity 0088 // TODO KF6 drop 0089 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0090 std::unique_ptr<QObject> qmlFontMetrics = nullptr; 0091 #endif 0092 0093 // Font metrics used for Units. 0094 // TextMetrics uses QFontMetricsF internally, so this should do the same 0095 QFontMetricsF fontMetrics; 0096 0097 // units 0098 int gridUnit; 0099 int smallSpacing; 0100 int mediumSpacing; 0101 int largeSpacing; 0102 0103 // durations 0104 int veryLongDuration; 0105 int longDuration; 0106 int shortDuration; 0107 int veryShortDuration; 0108 int humanMoment; 0109 int toolTipDelay; 0110 0111 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0112 int wheelScrollLines; 0113 #endif 0114 0115 IconSizes *const iconSizes; 0116 0117 // To prevent overriding custom set units if the font changes 0118 bool customUnitsSet = false; 0119 bool customWheelScrollLinesSet = false; 0120 0121 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0122 std::unique_ptr<QObject> createQmlFontMetrics(QQmlEngine *engine) 0123 { 0124 QQmlComponent component(engine); 0125 component.setData(QByteArrayLiteral( 0126 "import QtQuick 2.14\n" 0127 "import org.kde.kirigami 2.0\n" 0128 "FontMetrics {\n" 0129 " function roundedIconSize(size) {\n" 0130 R"( console.warn("Units.fontMetrics.roundedIconSize is deprecated, use Units.iconSizes.roundedIconSize instead.");)" 0131 " return Units.iconSizes.roundedIconSize(size)\n" 0132 " }\n" 0133 "}\n" 0134 ), QUrl(QStringLiteral("units.cpp"))); 0135 0136 return std::unique_ptr<QObject>(component.create()); 0137 } 0138 #endif 0139 }; 0140 0141 Units::~Units() = default; 0142 0143 Units::Units(QObject *parent) 0144 : QObject(parent) 0145 , d(std::make_unique<UnitsPrivate>(this)) 0146 { 0147 connect(QGuiApplication::styleHints(), &QStyleHints::wheelScrollLinesChanged, this, [this](int scrollLines) { 0148 if (d->customWheelScrollLinesSet) { 0149 return; 0150 } 0151 0152 setWheelScrollLines(scrollLines); 0153 }); 0154 connect(qGuiApp, &QGuiApplication::fontChanged, this, [this](const QFont &font) { 0155 d->fontMetrics = QFontMetricsF(font); 0156 0157 if (d->customUnitsSet) { 0158 return; 0159 } 0160 0161 d->gridUnit = d->fontMetrics.height(); 0162 Q_EMIT gridUnitChanged(); 0163 d->smallSpacing = std::floor(d->gridUnit / 4); 0164 Q_EMIT smallSpacingChanged(); 0165 d->mediumSpacing = std::round(d->smallSpacing * 1.5); 0166 Q_EMIT mediumSpacingChanged(); 0167 d->largeSpacing = d->smallSpacing * 2; 0168 Q_EMIT largeSpacingChanged(); 0169 Q_EMIT d->iconSizes->sizeForLabelsChanged(); 0170 }); 0171 } 0172 0173 qreal Units::devicePixelRatio() const 0174 { 0175 rateLimitWarning("Units.devicePixelRatio", "5.86", "This returns 1 when using Qt HiDPI scaling."); 0176 const int pixelSize = QGuiApplication::font().pixelSize(); 0177 const qreal pointSize = QGuiApplication::font().pointSize(); 0178 0179 return std::fmax(1, (pixelSize * 0.75 / pointSize)); 0180 } 0181 0182 int Units::gridUnit() const 0183 { 0184 return d->gridUnit; 0185 } 0186 0187 void Kirigami::Units::setGridUnit(int size) 0188 { 0189 if (d->gridUnit == size) { 0190 return; 0191 } 0192 0193 d->gridUnit = size; 0194 d->customUnitsSet = true; 0195 Q_EMIT gridUnitChanged(); 0196 } 0197 0198 int Units::smallSpacing() const 0199 { 0200 return d->smallSpacing; 0201 } 0202 0203 void Kirigami::Units::setSmallSpacing(int size) 0204 { 0205 if (d->smallSpacing == size) { 0206 return; 0207 } 0208 0209 d->smallSpacing = size; 0210 d->customUnitsSet = true; 0211 Q_EMIT smallSpacingChanged(); 0212 } 0213 0214 int Units::mediumSpacing() const 0215 { 0216 return d->mediumSpacing; 0217 } 0218 0219 void Kirigami::Units::setMediumSpacing(int size) 0220 { 0221 if (d->mediumSpacing == size) { 0222 return; 0223 } 0224 0225 d->mediumSpacing = size; 0226 d->customUnitsSet = true; 0227 Q_EMIT mediumSpacingChanged(); 0228 } 0229 0230 int Units::largeSpacing() const 0231 { 0232 return d->largeSpacing; 0233 } 0234 0235 void Kirigami::Units::setLargeSpacing(int size) 0236 { 0237 if (d->largeSpacing) { 0238 return; 0239 } 0240 0241 d->largeSpacing = size; 0242 d->customUnitsSet = true; 0243 Q_EMIT largeSpacingChanged(); 0244 } 0245 0246 int Units::veryLongDuration() const 0247 { 0248 return d->veryLongDuration; 0249 } 0250 0251 void Units::setVeryLongDuration(int duration) 0252 { 0253 if (d->veryLongDuration == duration) { 0254 return; 0255 } 0256 0257 d->veryLongDuration = duration; 0258 Q_EMIT veryLongDurationChanged(); 0259 } 0260 0261 int Units::longDuration() const 0262 { 0263 return d->longDuration; 0264 } 0265 0266 void Units::setLongDuration(int duration) 0267 { 0268 if (d->longDuration == duration) { 0269 return; 0270 } 0271 0272 d->longDuration = duration; 0273 Q_EMIT longDurationChanged(); 0274 } 0275 0276 int Units::shortDuration() const 0277 { 0278 return d->shortDuration; 0279 } 0280 0281 void Units::setShortDuration(int duration) 0282 { 0283 if (d->shortDuration == duration) { 0284 return; 0285 } 0286 0287 d->shortDuration = duration; 0288 Q_EMIT shortDurationChanged(); 0289 } 0290 0291 int Units::veryShortDuration() const 0292 { 0293 return d->veryShortDuration; 0294 } 0295 0296 void Units::setVeryShortDuration(int duration) 0297 { 0298 if (d->veryShortDuration == duration) { 0299 return; 0300 } 0301 0302 d->veryShortDuration = duration; 0303 Q_EMIT veryShortDurationChanged(); 0304 } 0305 0306 int Units::humanMoment() const 0307 { 0308 return d->humanMoment; 0309 } 0310 0311 void Units::setHumanMoment(int duration) 0312 { 0313 if (d->humanMoment == duration) { 0314 return; 0315 } 0316 0317 d->humanMoment = duration; 0318 Q_EMIT humanMomentChanged(); 0319 } 0320 0321 int Units::toolTipDelay() const 0322 { 0323 return d->toolTipDelay; 0324 } 0325 0326 void Units::setToolTipDelay(int delay) 0327 { 0328 if (d->toolTipDelay == delay) { 0329 return; 0330 } 0331 0332 d->toolTipDelay = delay; 0333 Q_EMIT toolTipDelayChanged(); 0334 } 0335 0336 int Units::maximumInteger() const 0337 { 0338 return std::numeric_limits<int>::max(); 0339 } 0340 0341 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0342 int Units::wheelScrollLines() const 0343 { 0344 rateLimitWarning("Units.wheelScrollLines", "5.86", "Use Qt.styleHints.wheelScrollLines instead"); 0345 return d->wheelScrollLines; 0346 } 0347 0348 void Units::setWheelScrollLines(int lines) 0349 { 0350 if (d->wheelScrollLines == lines) { 0351 return; 0352 } 0353 0354 d->wheelScrollLines = lines; 0355 d->customWheelScrollLinesSet = true; 0356 Q_EMIT wheelScrollLinesChanged(); 0357 } 0358 #endif 0359 0360 IconSizes *Units::iconSizes() const 0361 { 0362 return d->iconSizes; 0363 } 0364 0365 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86) 0366 QObject *Units::fontMetrics() const 0367 { 0368 rateLimitWarning("Units.fontMetrics", "5.86", "Create your own FontMetrics object instead."); 0369 if (!d->qmlFontMetrics) { 0370 d->qmlFontMetrics = d->createQmlFontMetrics(qmlEngine(this)); 0371 } 0372 return d->qmlFontMetrics.get(); 0373 } 0374 #endif 0375 0376 IconSizes::IconSizes(Units *units) 0377 : QObject(units) 0378 , m_units(units) 0379 { 0380 } 0381 0382 int IconSizes::roundedIconSize(int size) const 0383 { 0384 if (size < 16) { 0385 return size; 0386 } 0387 0388 if (size < 22) { 0389 return 16; 0390 } 0391 0392 if (size < 32) { 0393 return 22; 0394 } 0395 0396 if (size < 48) { 0397 return 32; 0398 } 0399 0400 if (size < 64) { 0401 return 48; 0402 } 0403 0404 return size; 0405 } 0406 0407 int IconSizes::sizeForLabels() const 0408 { 0409 // gridUnit is the height of textMetrics 0410 return roundedIconSize(m_units->d->fontMetrics.height()); 0411 } 0412 0413 int IconSizes::small() const 0414 { 0415 return 16; 0416 } 0417 0418 int IconSizes::smallMedium() const 0419 { 0420 return 22; 0421 } 0422 0423 int IconSizes::medium() const 0424 { 0425 return 32; 0426 } 0427 0428 int IconSizes::large() const 0429 { 0430 return 48; 0431 } 0432 0433 int IconSizes::huge() const 0434 { 0435 return 64; 0436 } 0437 0438 int IconSizes::enormous() const 0439 { 0440 return 128; 0441 } 0442 0443 } 0444 0445 #include "moc_units.cpp"