1 /*
2     SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
3     SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #ifndef UNITS_H
9 #define UNITS_H
10 
11 #include <QObject>
12 #include <QQmlPropertyMap>
13 
14 #include <Plasma/Theme>
15 
16 #include <KConfigWatcher>
17 
18 class QQuickItem;
19 
20 class SharedAppFilter : public QObject
21 {
22     Q_OBJECT
23 public:
24     explicit SharedAppFilter(QObject *parent = nullptr);
25     ~SharedAppFilter() override;
26 
27 Q_SIGNALS:
28     void fontChanged();
29 
30 protected:
31     bool eventFilter(QObject *watched, QEvent *event) override;
32 };
33 
34 /**
35  * @class Units
36  * @short Expose sizes to QML
37  */
38 class Units : public QObject
39 {
40     Q_OBJECT
41 
42     /**
43      * The fundamental unit of space that should be used for sizes, expressed in pixels.
44      * Given the screen has an accurate DPI settings, it corresponds to the height of
45      * the font's boundingRect.
46      */
47     Q_PROPERTY(int gridUnit READ gridUnit NOTIFY gridUnitChanged)
48 
49     /**
50      * units.iconSizes provides access to platform-dependent icon sizing
51      *
52      * The icon sizes provided are normalized for different DPI, so icons
53      * will scale depending on the DPI.
54      *
55      * Icon sizes from KIconLoader, adjusted to devicePixelRatio:
56      * * small
57      * * smallMedium
58      * * medium
59      * * large
60      * * huge
61      * * enormous
62      * * desktop (DEPRECATED: use iconSizeHints instead)
63      *
64      */
65     // note the iconSizeChanged signal indicates that one (or more) of these icons have changed
66     // but the property map itself remains constant
67     Q_PROPERTY(QQmlPropertyMap *iconSizes READ iconSizes CONSTANT)
68 
69     /**
70      * units.iconSizeHints provides access to user-configurable icon size hints,
71      * to be used where appropriate in the user interface.
72      *
73      * Conceptually, an icon size hint is a key that has one of the sizes from
74      * @iconSizes property as value.
75      *
76      * Currently available hints:
77      * * panel
78      * * desktop
79      */
80     // note the iconSizeHintsChanged signal indicates that one (or more) of these icons have changed
81     // but the property map itself remains constant
82     Q_PROPERTY(QQmlPropertyMap *iconSizeHints READ iconSizeHints CONSTANT)
83 
84     // layout hints
85 
86     /**
87      * units.smallSpacing is the amount of spacing that should be used around smaller UI elements,
88      * for example as spacing in Columns. Internally, this size depends on the size of
89      * the default font as rendered on the screen, so it takes user-configured font size and DPI
90      * into account.
91      */
92     Q_PROPERTY(int smallSpacing READ smallSpacing NOTIFY spacingChanged)
93 
94     /**
95      * units.largeSpacing is the amount of spacing that should be used inside bigger UI elements,
96      * for example between an icon and the corresponding text. Internally, this size depends on
97      * the size of the default font as rendered on the screen, so it takes user-configured font
98      * size and DPI into account.
99      */
100     Q_PROPERTY(int largeSpacing READ largeSpacing NOTIFY spacingChanged)
101 
102     /**
103      * The ratio between physical and device-independent pixels. This value does not depend on the \
104      * size of the configured font. If you want to take font sizes into account when scaling elements,
105      * use PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont), PlasmaCore.Units.smallSpacing and PlasmaCore.Units.largeSpacing.
106      * The devicePixelRatio follows the definition of "device independent pixel" by Microsoft.
107      */
108     Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged)
109 
110     /**
111      * units.longDuration should be used for longer, screen-covering animations, for opening and
112      * closing of dialogs and other "not too small" animations
113      */
114     Q_PROPERTY(int longDuration READ longDuration NOTIFY durationChanged)
115 
116     /**
117      * units.shortDuration should be used for short animations, such as accentuating a UI event,
118      * hover events, etc..
119      */
120     Q_PROPERTY(int shortDuration READ shortDuration NOTIFY durationChanged)
121 
122     /**
123      * units.veryShortDuration should be used for elements that should animate near instantly,
124      * but should have a hint of smoothness
125      */
126     Q_PROPERTY(int veryShortDuration READ veryShortDuration NOTIFY durationChanged)
127 
128     /**
129      * units.veryLongDuration should be used for specialty animations that benefit
130      * from being even longer than longDuration.
131      */
132     Q_PROPERTY(int veryLongDuration READ veryLongDuration NOTIFY durationChanged)
133 
134     /**
135      * Time in milliseconds equivalent to the theoretical human moment, which can be used
136      * to determine whether how long to wait until the user should be informed of something,
137      * or can be used as the limit for how long something should wait before being
138      * automatically initiated.
139      *
140      * Some examples:
141      *
142      * - When the user types text in a search field, wait no longer than this duration after
143      *   the user completes typing before starting the search
144      * - When loading data which would commonly arrive rapidly enough to not require interaction,
145      *   wait this long before showing a spinner
146      *
147      * This might seem an arbitrary number, but given the psychological effect that three
148      * seconds seems to be what humans consider a moment (and in the case of waiting for
149      * something to happen, a moment is that time when you think "this is taking a bit long,
150      * isn't it?"), the idea is to postpone for just before such a conceptual moment. The reason
151      * for the two seconds, rather than three, is to function as a middle ground: Not long enough
152      * that the user would think that something has taken too long, for also not so fast as to
153      * happen too soon.
154      *
155      * See also
156      * https://www.psychologytoday.com/blog/all-about-addiction/201101/tick-tock-tick-hugs-and-life-in-3-second-intervals
157      * (the actual paper is hidden behind an academic paywall and consequently not readily
158      * available to us, so the source will have to be the blog entry above)
159      *
160      * @since 5.81
161      */
162     Q_PROPERTY(int humanMoment READ humanMoment CONSTANT)
163 
164 public:
165     /// @cond INTERNAL_DOCS
166 
167     ~Units() override;
168 
169     /**
170      * @return a reference to the global Units instance
171      * @since 5.31
172      */
173     static Units &instance();
174 
175     /**
176      * @return pixel value for a grid Unit. Depends on DPI and font size.
177      */
178     int gridUnit() const;
179 
180     /**
181      * @return The ratio between physical and device-independent pixels.
182      */
183     qreal devicePixelRatio() const;
184 
185     /**
186      * @return map with iconsizes, indexed by name
187      */
188     QQmlPropertyMap *iconSizes() const;
189 
190     /**
191      * @return map with user-configurable icon size hints, indexed by name
192      * @since 5.33
193      */
194     QQmlPropertyMap *iconSizeHints() const;
195 
196     /**
197      * @return Pixel value for large spacing between elements.
198      * @since 5.0
199      */
200     int smallSpacing() const;
201 
202     /**
203      * @return Pixel value for large spacing between elements.
204      * @since 5.0
205      */
206     int largeSpacing() const;
207 
208     /**
209      * @return Duration for long animations, in milliseconds.
210      * @since 5.0
211      */
212     int longDuration() const;
213 
214     /**
215      * @return Duration for short animations, in milliseconds.
216      * @since 5.0
217      */
218     int shortDuration() const;
219 
220     /**
221      * @return Duration for very long animations, in milliseconds.
222      * @since 5.69
223      */
224     int veryLongDuration() const;
225 
226     /**
227      * @return Duration for instantaneous animations, in milliseconds.
228      * @since 5.78
229      */
230     int veryShortDuration() const;
231 
232     /**
233      * @return Duration for very long wait times
234      * @since 5.81
235      */
236     int humanMoment() const;
237     /// @endcond
238 
239     /**
240      * @return a size rounded to the nearest inferior standard icon size.
241      *           sizes larger than iconSizes.huge, it will be returned unmodified
242      * @param int size the size we want to be rounded down
243      * @see iconSizes
244      */
245     Q_INVOKABLE static int roundToIconSize(int size);
246 
247 Q_SIGNALS:
248     void devicePixelRatioChanged();
249     void gridUnitChanged();
250     void iconSizesChanged();
251     void iconSizeHintsChanged();
252     void spacingChanged();
253     void durationChanged();
254 
255 private Q_SLOTS:
256     void iconLoaderSettingsChanged();
257     void updateSpacing();
258 
259 private:
260     Units(QObject *parent = nullptr);
261     Units(Units const &) = delete; // Copy construct
262     Units(Units &&) = delete; // Move construct
263     Units &operator=(Units const &) = delete; // Copy assign
264     Units &operator=(Units &&) = delete; // Move assign
265 
266     void updateDevicePixelRatio();
267     void updateAnimationSpeed();
268 
269     /**
270      * @return the best-looking icon scale for the given device pixel ratio
271      * Note that this function is only relevant when using Plasma scaling and
272      * when using Qt scaling, it always returns 1.
273      */
274     static qreal bestIconScaleForDevicePixelRatio(const qreal ratio);
275 
276     /**
277      * @return The dpi-adjusted size for a given icon size
278      */
279     int devicePixelIconSize(const int size) const;
280 
281     int m_gridUnit;
282     qreal m_devicePixelRatio;
283 
284     QQmlPropertyMap *m_iconSizes;
285     QQmlPropertyMap *m_iconSizeHints;
286     static SharedAppFilter *s_sharedAppFilter;
287 
288     int m_smallSpacing;
289     int m_largeSpacing;
290 
291     KConfigWatcher::Ptr m_animationSpeedWatcher;
292     int m_longDuration;
293 };
294 
295 #endif // UNITS_H
296