Plasma

core/units.cpp
1 /*
2  SPDX-FileCopyrightText: 2013 Marco Martin <[email protected]>
3  SPDX-FileCopyrightText: 2014 Sebastian Kügler <[email protected]>
4  SPDX-FileCopyrightText: 2014 David Edmundson <[email protected]>
5 
6  SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "units.h"
10 
11 #include <QDebug>
12 #include <QFontMetrics>
13 #include <QGuiApplication>
14 #include <QQuickItem>
15 #include <QQuickWindow>
16 #include <QScreen>
17 #include <QtGlobal>
18 #include <cmath>
19 
20 #include <KIconLoader>
21 
22 const int defaultLongDuration = 200;
23 
24 SharedAppFilter::SharedAppFilter(QObject *parent)
25  : QObject(parent)
26 {
28 }
29 
30 SharedAppFilter::~SharedAppFilter()
31 {
32 }
33 
34 bool SharedAppFilter::eventFilter(QObject *watched, QEvent *event)
35 {
36  if (watched == QCoreApplication::instance()) {
37  if (event->type() == QEvent::ApplicationFontChange) {
38  Q_EMIT fontChanged();
39  }
40  }
41  return QObject::eventFilter(watched, event);
42 }
43 
44 SharedAppFilter *Units::s_sharedAppFilter = nullptr;
45 
46 Units::Units(QObject *parent)
47  : QObject(parent)
48  , m_gridUnit(-1)
49  , m_devicePixelRatio(-1)
50  , m_smallSpacing(-1)
51  , m_mediumSpacing(-1)
52  , m_largeSpacing(-1)
53  , m_longDuration(defaultLongDuration) // default base value for animations
54 {
55  if (!s_sharedAppFilter) {
56  s_sharedAppFilter = new SharedAppFilter();
57  }
58 
59  m_iconSizes = new QQmlPropertyMap(this);
60  m_iconSizeHints = new QQmlPropertyMap(this);
61  updateDevicePixelRatio(); // also updates icon sizes
62  updateSpacing(); // updates gridUnit and *Spacing properties
63 
64  connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &Units::iconLoaderSettingsChanged);
65  QObject::connect(s_sharedAppFilter, &SharedAppFilter::fontChanged, this, &Units::updateSpacing);
66 
67  m_animationSpeedWatcher = KConfigWatcher::create(KSharedConfig::openConfig());
68  connect(m_animationSpeedWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) {
69  if (group.name() == QLatin1String("KDE") && names.contains(QByteArrayLiteral("AnimationDurationFactor"))) {
70  updateAnimationSpeed();
71  }
72  });
73  updateAnimationSpeed();
74 }
75 
76 Units::~Units()
77 {
78 }
79 
80 Units &Units::instance()
81 {
82  static Units units;
83  return units;
84 }
85 
86 void Units::updateAnimationSpeed()
87 {
88  KConfigGroup generalCfg = KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("KDE"));
89  const qreal animationSpeedModifier = qMax(0.0, generalCfg.readEntry("AnimationDurationFactor", 1.0));
90 
91  // Read the old longDuration value for compatibility
92  KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Units"));
93  int longDuration = cfg.readEntry("longDuration", defaultLongDuration);
94 
95  longDuration = qRound(longDuration * animationSpeedModifier);
96 
97  // Animators with a duration of 0 do not fire reliably
98  // see Bug 357532 and QTBUG-39766
99  longDuration = qMax(1, longDuration);
100 
101  if (longDuration != m_longDuration) {
102  m_longDuration = longDuration;
103  Q_EMIT durationChanged();
104  }
105 }
106 
107 void Units::iconLoaderSettingsChanged()
108 {
109  m_iconSizes->insert(QStringLiteral("desktop"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Desktop)));
110 
111  m_iconSizes->insert(QStringLiteral("tiny"), devicePixelIconSize(KIconLoader::SizeSmall) / 2);
112  m_iconSizes->insert(QStringLiteral("small"), devicePixelIconSize(KIconLoader::SizeSmall));
113  m_iconSizes->insert(QStringLiteral("smallMedium"), devicePixelIconSize(KIconLoader::SizeSmallMedium));
114  m_iconSizes->insert(QStringLiteral("medium"), devicePixelIconSize(KIconLoader::SizeMedium));
115  m_iconSizes->insert(QStringLiteral("large"), devicePixelIconSize(KIconLoader::SizeLarge));
116  m_iconSizes->insert(QStringLiteral("huge"), devicePixelIconSize(KIconLoader::SizeHuge));
117  m_iconSizes->insert(QStringLiteral("enormous"), devicePixelIconSize(KIconLoader::SizeEnormous));
118  // We deliberately don't feed the result into devicePixelIconSize() because
119  // roundToIconSize() already does that internally.
120  m_iconSizes->insert(QStringLiteral("sizeForLabels"), roundToIconSize(QFontMetrics(QGuiApplication::font()).height()));
121 
122  m_iconSizeHints->insert(QStringLiteral("panel"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Panel)));
123  m_iconSizeHints->insert(QStringLiteral("desktop"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Desktop)));
124 
125  Q_EMIT iconSizesChanged();
126  Q_EMIT iconSizeHintsChanged();
127 }
128 
130 {
131  return m_iconSizes;
132 }
133 
135 {
136  return m_iconSizeHints;
137 }
138 
140 {
141  // If not using Qt scaling (e.g. on X11 by default), manually scale all sizes
142  // according to the DPR because Qt hasn't done it for us. Otherwise all
143  // returned sizes are too small and icons are only the right size by pure
144  // chance for the larger sizes due to their large differences in size
145  qreal multiplier = 1.0;
147  if (primary) {
148  // Note that when using Qt scaling, the multiplier will always be 1
149  // because Qt will scale everything for us. This is good and intentional.
150  multiplier = bestIconScaleForDevicePixelRatio((qreal)primary->logicalDotsPerInchX() / (qreal)96);
151  }
152 
153  if (size <= 0) {
154  return 0;
155 
156  } else if (size < KIconLoader::SizeSmall * multiplier) {
157  return qRound(KIconLoader::SizeSmall * multiplier);
158 
159  } else if (size < KIconLoader::SizeSmallMedium * multiplier) {
160  return qRound(KIconLoader::SizeSmall * multiplier);
161 
162  } else if (size < KIconLoader::SizeMedium * multiplier) {
163  return qRound(KIconLoader::SizeSmallMedium * multiplier);
164 
165  } else if (size < KIconLoader::SizeLarge * multiplier) {
166  return qRound(KIconLoader::SizeMedium * multiplier);
167 
168  } else if (size < KIconLoader::SizeHuge * multiplier) {
169  return qRound(KIconLoader::SizeLarge * multiplier);
170 
171  } else if (size < KIconLoader::SizeEnormous * multiplier) {
172  return qRound(KIconLoader::SizeHuge * multiplier);
173 
174  } else {
175  return size;
176  }
177 }
178 
179 qreal Units::bestIconScaleForDevicePixelRatio(const qreal ratio)
180 {
181  if (ratio < 1.5) {
182  return 1;
183  } else if (ratio < 2.0) {
184  return 1.5;
185  } else if (ratio < 2.5) {
186  return 2.0;
187  } else if (ratio < 3.0) {
188  return 2.5;
189  } else if (ratio < 3.5) {
190  return 3.0;
191  } else {
192  return ratio;
193  }
194 }
195 int Units::devicePixelIconSize(const int size) const
196 {
197  /* in kiconloader.h
198  enum StdSizes {
199  SizeSmall=16,
200  SizeSmallMedium=22,
201  SizeMedium=32,
202  SizeLarge=48,
203  SizeHuge=64,
204  SizeEnormous=128
205  };
206  */
207  // Scale the icon sizes up using the devicePixelRatio
208  // This function returns the next stepping icon size
209  // and multiplies the global settings with the dpi ratio.
210  const qreal ratio = devicePixelRatio();
211 
212  return size * bestIconScaleForDevicePixelRatio(ratio);
213 }
214 
215 qreal Units::devicePixelRatio() const
216 {
217  return m_devicePixelRatio;
218 }
219 
220 void Units::updateDevicePixelRatio()
221 {
222  // Using QGuiApplication::devicePixelRatio() gives too coarse values,
223  // i.e. it directly jumps from 1.0 to 2.0. We want tighter control on
224  // sizing, so we compute the exact ratio and use that.
225  // TODO: make it possible to adapt to the dpi for the current screen dpi
226  // instead of assuming that all of them use the same dpi which applies for
227  // X11 but not for other systems.
229  if (!primary) {
230  return;
231  }
232  const qreal dpi = primary->logicalDotsPerInchX();
233  // Usual "default" is 96 dpi
234  // that magic ratio follows the definition of "device independent pixel" by Microsoft
235  m_devicePixelRatio = (qreal)dpi / (qreal)96;
236  iconLoaderSettingsChanged();
237  Q_EMIT devicePixelRatioChanged();
238 }
239 
240 int Units::gridUnit() const
241 {
242  return m_gridUnit;
243 }
244 
245 int Units::smallSpacing() const
246 {
247  return m_smallSpacing;
248 }
249 
250 int Units::mediumSpacing() const
251 {
252  return m_mediumSpacing;
253 }
254 
255 int Units::largeSpacing() const
256 {
257  return m_largeSpacing;
258 }
259 
260 void Units::updateSpacing()
261 {
262  int gridUnit = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height();
263 
264  if (gridUnit % 2 != 0) {
265  gridUnit++;
266  }
267  if (gridUnit != m_gridUnit) {
268  m_gridUnit = gridUnit;
269  Q_EMIT gridUnitChanged();
270  }
271 
272  if (gridUnit != m_largeSpacing) {
273  m_smallSpacing = qMax(2, (int)(gridUnit / 4)); // 1/4 of gridUnit, at least 2
274  m_mediumSpacing = std::round(m_smallSpacing * 1.5);
275  m_largeSpacing = gridUnit; // msize.height
276  Q_EMIT spacingChanged();
277  }
278 }
279 
280 int Units::longDuration() const
281 {
282  return m_longDuration;
283 }
284 
285 int Units::shortDuration() const
286 {
287  return qMax(1, qRound(m_longDuration * 0.5));
288 }
289 
290 int Units::veryShortDuration() const
291 {
292  return qRound(m_longDuration * 0.25);
293 }
294 
295 int Units::veryLongDuration() const
296 {
297  return m_longDuration * 2;
298 }
299 
300 int Units::humanMoment() const
301 {
302  return 2000;
303 }
304 
305 #include "moc_units.cpp"
int gridUnit
The fundamental unit of space that should be used for sizes, expressed in pixels.
Definition: core/units.h:47
Expose sizes to QML.
Definition: core/units.h:38
QString readEntry(const char *key, const char *aDefault=nullptr) const
void insert(const QString &key, const QVariant &value)
int shortDuration
units.shortDuration should be used for short animations, such as accentuating a UI event,...
Definition: core/units.h:127
ApplicationFontChange
Q_EMITQ_EMIT
int veryLongDuration
units.veryLongDuration should be used for specialty animations that benefit from being even longer th...
Definition: core/units.h:139
int longDuration
units.longDuration should be used for longer, screen-covering animations, for opening and closing of ...
Definition: core/units.h:121
void configChanged(const KConfigGroup &group, const QByteArrayList &names)
QRect boundingRect(QChar ch) const const
static Q_INVOKABLE int roundToIconSize(int size)
Definition: core/units.cpp:139
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
int largeSpacing
This property is functionally identical to gridUnit.
Definition: core/units.h:107
virtual bool eventFilter(QObject *watched, QEvent *event)
int mediumSpacing
This property holds the amount of spacing that should be used between medium UI elements,...
Definition: core/units.h:100
void installEventFilter(QObject *filterObj)
QCoreApplication * instance()
static Ptr create(const KSharedConfig::Ptr &config)
qreal devicePixelRatio
The ratio between physical and device-independent pixels.
Definition: core/units.h:115
static KIconLoader * global()
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
int smallSpacing
This property holds the amount of spacing that should be used between smaller UI elements,...
Definition: core/units.h:92
QQmlPropertyMap iconSizeHints
units.iconSizeHints provides access to user-configurable icon size hints, to be used where appropriat...
Definition: core/units.h:82
void iconLoaderSettingsChanged()
int height() const const
QQmlPropertyMap iconSizes
units.iconSizes provides access to platform-dependent icon sizing
Definition: core/units.h:67
logicalDotsPerInchX
int humanMoment
Time in milliseconds equivalent to the theoretical human moment, which can be used to determine wheth...
Definition: core/units.h:169
int veryShortDuration
units.veryShortDuration should be used for elements that should animate near instantly,...
Definition: core/units.h:133
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 04:07:39 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.