Kirigami2

units.cpp
1 /*
2  * SPDX-FileCopyrightText: 2020 Jonah BrĂ¼chert <[email protected]>
3  * SPDX-FileCopyrightText: 2015 Marco Martin <[email protected]>
4  *
5  * SPDX-License-Identifier: LGPL-2.0-or-later
6  */
7 
8 #include "units.h"
9 
10 #include <QFont>
11 #include <QFontMetrics>
12 #include <QGuiApplication>
13 #include <QQmlComponent>
14 #include <QQmlEngine>
15 #include <QStyleHints>
16 
17 #include <chrono>
18 #include <cmath>
19 
20 #include "loggingcategory.h"
21 
22 namespace Kirigami {
23 using clock = std::chrono::steady_clock;
24 
25 const clock::duration rateLimit = std::chrono::seconds(1);
26 
27 /* Print a deprecation warning that is rate limited to only display once in
28  * every time period as determined by rateLimit. We keep track of how often this
29  * is called and display that if it is larger than 0.
30  *
31  * This is done to prevent flooding the logs with "X is deprecated" messages
32  * that are all the same and don't provide any new information after the first.
33  */
34 void rateLimitWarning(const char *method, const char *since, const char *message)
35 {
37 
38  auto methodString = QString::fromUtf8(method);
39 
40  if (!messages.contains(methodString)) {
41  messages.insert(methodString, qMakePair(clock::time_point{}, 0));
42  }
43 
44  auto entry = messages.value(methodString);
45  if (clock::now() - entry.first < rateLimit) {
46  messages[methodString].second += 1;
47  return;
48  }
49 
50  qCWarning(KirigamiLog).nospace() << method << " is deprecated (since " << since << "): " << message;
51 
52  if (entry.second > 0) {
53  qCWarning(KirigamiLog) << "Previous message repeats" << entry.second << "times.";
54  }
55 
56  messages[methodString] = qMakePair(clock::now(), 0);
57 }
58 
59 class UnitsPrivate
60 {
61  Q_DISABLE_COPY(UnitsPrivate)
62 
63 public:
64  explicit UnitsPrivate(Units *units)
65 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
66  : qmlFontMetrics(nullptr)
67 #endif
68  // Cache font so we don't have to go through QVariant and property every time
69  , fontMetrics(QFontMetricsF(QGuiApplication::font()))
70  , gridUnit(fontMetrics.height())
71  , smallSpacing(std::floor(gridUnit / 4))
72  , mediumSpacing(std::round(smallSpacing * 1.5))
73  , largeSpacing(smallSpacing * 2)
74  , veryLongDuration(400)
75  , longDuration(200)
76  , shortDuration(100)
77  , veryShortDuration(50)
78  , humanMoment(2000)
79  , toolTipDelay(700)
80 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
81  , wheelScrollLines(QGuiApplication::styleHints()->wheelScrollLines())
82 #endif
83  , iconSizes(new IconSizes(units))
84  {
85  }
86 
87  // Only stored for QML API compatiblity
88  // TODO KF6 drop
89 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
90  std::unique_ptr<QObject> qmlFontMetrics = nullptr;
91 #endif
92 
93  // Font metrics used for Units.
94  // TextMetrics uses QFontMetricsF internally, so this should do the same
95  QFontMetricsF fontMetrics;
96 
97  // units
98  int gridUnit;
99  int smallSpacing;
100  int mediumSpacing;
101  int largeSpacing;
102 
103  // durations
104  int veryLongDuration;
105  int longDuration;
106  int shortDuration;
107  int veryShortDuration;
108  int humanMoment;
109  int toolTipDelay;
110 
111 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
112  int wheelScrollLines;
113 #endif
114 
115  IconSizes *const iconSizes;
116 
117  // To prevent overriding custom set units if the font changes
118  bool customUnitsSet = false;
119  bool customWheelScrollLinesSet = false;
120 
121 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
122  std::unique_ptr<QObject> createQmlFontMetrics(QQmlEngine *engine)
123  {
124  QQmlComponent component(engine);
125  component.setData(QByteArrayLiteral(
126  "import QtQuick 2.14\n"
127  "import org.kde.kirigami 2.0\n"
128  "FontMetrics {\n"
129  " function roundedIconSize(size) {\n"
130  R"( console.warn("Units.fontMetrics.roundedIconSize is deprecated, use Units.iconSizes.roundedIconSize instead.");)"
131  " return Units.iconSizes.roundedIconSize(size)\n"
132  " }\n"
133  "}\n"
134  ), QUrl(QStringLiteral("units.cpp")));
135 
136  return std::unique_ptr<QObject>(component.create());
137  }
138 #endif
139 };
140 
141 Units::~Units() = default;
142 
143 Units::Units(QObject *parent)
144  : QObject(parent)
145  , d(std::make_unique<UnitsPrivate>(this))
146 {
148  if (d->customWheelScrollLinesSet) {
149  return;
150  }
151 
152  setWheelScrollLines(scrollLines);
153  });
154  connect(qGuiApp, &QGuiApplication::fontChanged, this, [this](const QFont &font) {
155  d->fontMetrics = QFontMetricsF(font);
156 
157  if (d->customUnitsSet) {
158  return;
159  }
160 
161  d->gridUnit = d->fontMetrics.height();
162  Q_EMIT gridUnitChanged();
163  d->smallSpacing = std::floor(d->gridUnit / 4);
164  Q_EMIT smallSpacingChanged();
165  d->mediumSpacing = std::round(d->smallSpacing * 1.5);
166  Q_EMIT mediumSpacingChanged();
167  d->largeSpacing = d->smallSpacing * 2;
168  Q_EMIT largeSpacingChanged();
169  Q_EMIT d->iconSizes->sizeForLabelsChanged();
170  });
171 }
172 
173 qreal Units::devicePixelRatio() const
174 {
175  rateLimitWarning("Units.devicePixelRatio", "5.86", "This returns 1 when using Qt HiDPI scaling.");
176  const int pixelSize = QGuiApplication::font().pixelSize();
177  const qreal pointSize = QGuiApplication::font().pointSize();
178 
179  return std::fmax(1, (pixelSize * 0.75 / pointSize));
180 }
181 
182 int Units::gridUnit() const
183 {
184  return d->gridUnit;
185 }
186 
187 void Kirigami::Units::setGridUnit(int size)
188 {
189  if (d->gridUnit == size) {
190  return;
191  }
192 
193  d->gridUnit = size;
194  d->customUnitsSet = true;
195  Q_EMIT gridUnitChanged();
196 }
197 
198 int Units::smallSpacing() const
199 {
200  return d->smallSpacing;
201 }
202 
203 void Kirigami::Units::setSmallSpacing(int size)
204 {
205  if (d->smallSpacing == size) {
206  return;
207  }
208 
209  d->smallSpacing = size;
210  d->customUnitsSet = true;
211  Q_EMIT smallSpacingChanged();
212 }
213 
214 int Units::mediumSpacing() const
215 {
216  return d->mediumSpacing;
217 }
218 
219 void Kirigami::Units::setMediumSpacing(int size)
220 {
221  if (d->mediumSpacing == size) {
222  return;
223  }
224 
225  d->mediumSpacing = size;
226  d->customUnitsSet = true;
227  Q_EMIT mediumSpacingChanged();
228 }
229 
230 int Units::largeSpacing() const
231 {
232  return d->largeSpacing;
233 }
234 
235 void Kirigami::Units::setLargeSpacing(int size)
236 {
237  if (d->largeSpacing) {
238  return;
239  }
240 
241  d->largeSpacing = size;
242  d->customUnitsSet = true;
243  Q_EMIT largeSpacingChanged();
244 }
245 
246 int Units::veryLongDuration() const
247 {
248  return d->veryLongDuration;
249 }
250 
251 void Units::setVeryLongDuration(int duration)
252 {
253  if (d->veryLongDuration == duration) {
254  return;
255  }
256 
257  d->veryLongDuration = duration;
258  Q_EMIT veryLongDurationChanged();
259 }
260 
261 int Units::longDuration() const
262 {
263  return d->longDuration;
264 }
265 
266 void Units::setLongDuration(int duration)
267 {
268  if (d->longDuration == duration) {
269  return;
270  }
271 
272  d->longDuration = duration;
273  Q_EMIT longDurationChanged();
274 }
275 
276 int Units::shortDuration() const
277 {
278  return d->shortDuration;
279 }
280 
281 void Units::setShortDuration(int duration)
282 {
283  if (d->shortDuration == duration) {
284  return;
285  }
286 
287  d->shortDuration = duration;
288  Q_EMIT shortDurationChanged();
289 }
290 
291 int Units::veryShortDuration() const
292 {
293  return d->veryLongDuration;
294 }
295 
296 void Units::setVeryShortDuration(int duration)
297 {
298  if (d->veryLongDuration == duration) {
299  return;
300  }
301 
302  d->veryLongDuration = duration;
303  Q_EMIT veryShortDurationChanged();
304 }
305 
306 int Units::humanMoment() const
307 {
308  return d->humanMoment;
309 }
310 
311 void Units::setHumanMoment(int duration)
312 {
313  if (d->humanMoment == duration) {
314  return;
315  }
316 
317  d->humanMoment = duration;
318  Q_EMIT humanMomentChanged();
319 }
320 
321 int Units::toolTipDelay() const
322 {
323  return d->toolTipDelay;
324 }
325 
326 void Units::setToolTipDelay(int delay)
327 {
328  if (d->toolTipDelay == delay) {
329  return;
330  }
331 
332  d->toolTipDelay = delay;
333  Q_EMIT toolTipDelayChanged();
334 }
335 
336 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
337 int Units::wheelScrollLines() const
338 {
339  rateLimitWarning("Units.wheelScrollLines", "5.86", "Use Qt.styleHints.wheelScrollLines instead");
340  return d->wheelScrollLines;
341 }
342 
343 void Units::setWheelScrollLines(int lines)
344 {
345  if (d->wheelScrollLines == lines) {
346  return;
347  }
348 
349  d->wheelScrollLines = lines;
350  d->customWheelScrollLinesSet = true;
351  Q_EMIT wheelScrollLinesChanged();
352 }
353 #endif
354 
355 IconSizes *Units::iconSizes() const
356 {
357  return d->iconSizes;
358 }
359 
360 #if KIRIGAMI2_BUILD_DEPRECATED_SINCE(5, 86)
361 QObject *Units::fontMetrics() const
362 {
363  rateLimitWarning("Units.fontMetrics", "5.86", "Create your own FontMetrics object instead.");
364  if (!d->qmlFontMetrics) {
365  d->qmlFontMetrics = d->createQmlFontMetrics(qmlEngine(this));
366  }
367  return d->qmlFontMetrics.get();
368 }
369 #endif
370 
371 IconSizes::IconSizes(Units *units)
372  : QObject(units)
373  , m_units(units)
374 {
375 }
376 
377 int IconSizes::roundedIconSize(int size) const
378 {
379  if (size < 16) {
380  return size;
381  }
382 
383  if (size < 22) {
384  return 16;
385  }
386 
387  if (size < 32) {
388  return 22;
389  }
390 
391  if (size < 48) {
392  return 32;
393  }
394 
395  if (size < 64) {
396  return 48;
397  }
398 
399  return size;
400 }
401 
402 int IconSizes::sizeForLabels() const
403 {
404  // gridUnit is the height of textMetrics
405  return roundedIconSize(m_units->d->fontMetrics.height());
406 }
407 
408 int IconSizes::small() const
409 {
410  return 16;
411 }
412 
413 int IconSizes::smallMedium() const
414 {
415  return 22;
416 }
417 
418 int IconSizes::medium() const
419 {
420  return 32;
421 }
422 
423 int IconSizes::large() const
424 {
425  return 48;
426 }
427 
428 int IconSizes::huge() const
429 {
430  return 64;
431 }
432 
433 int IconSizes::enormous() const
434 {
435  return 128;
436 }
437 
438 }
int gridUnit
bool contains(const Key &key) const const
int veryShortDuration
units.veryShortDuration should be used for elements that should have a hint of smoothness,...
Definition: units.h:157
IconSizes iconSizes
units.iconSizes provides access to platform-dependent icon sizing
Definition: units.h:98
QString fromUtf8(const char *str, int size)
int pixelSize() const const
Q_EMITQ_EMIT
const T value(const Key &key, const T &defaultValue) const const
int pointSize() const const
void wheelScrollLinesChanged(int scrollLines)
int toolTipDelay
time in ms by which the display of tooltips will be delayed.
Definition: units.h:200
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QMap::iterator insert(const Key &key, const T &value)
int veryLongDuration
units.veryLongDuration should be used for specialty animations that benefit from being even longer th...
Definition: units.h:139
void fontChanged(const QFont &font)
int mediumSpacing
This property holds the amount of spacing that should be used between medium UI elements,...
Definition: units.h:114
int shortDuration
units.shortDuration should be used for short animations, such as accentuating a UI event,...
Definition: units.h:151
int smallSpacing
This property holds the amount of spacing that should be used between smaller UI elements,...
Definition: units.h:106
qreal devicePixelRatio
QStyleHints * styleHints()
int humanMoment
Time in milliseconds equivalent to the theoretical human moment, which can be used to determine wheth...
Definition: units.h:193
int longDuration
units.longDuration should be used for longer, screen-covering animations, for opening and closing of ...
Definition: units.h:145
int largeSpacing
This property holds the amount of spacing that should be used between bigger UI elements,...
Definition: units.h:122
QString message
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri May 20 2022 04:05:12 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.