Perceptual Color

helper.h
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4#ifndef HELPER_H
5#define HELPER_H
6
7#include "helpermath.h"
8#include "helperqttypes.h"
9#include <QtCore/qsharedpointer.h>
10#include <optional>
11#include <qcontainerfwd.h>
12#include <qcoreapplication.h>
13#include <qglobal.h>
14#include <qicon.h>
15#include <qimage.h>
16#include <qlist.h>
17#include <qmetaobject.h>
18#include <qpair.h>
19#include <qstring.h>
20#include <qstringliteral.h>
21#include <qthread.h>
22
23class QColor;
24class QWheelEvent;
25class QWidget;
26
27namespace PerceptualColor
28{
29
30class RgbColorSpace;
31
32/** @brief Represents the appearance of a theme. */
33enum class ColorSchemeType {
34 Light, /**< Light theme. */
35 Dark /**< Dark theme. */
36};
37
38void drawQWidgetStyleSheetAware(QWidget *widget);
39
40QString fromMnemonicToRichText(const QString &mnemonicText);
41
42std::optional<ColorSchemeType> guessColorSchemeTypeFromWidget(QWidget *widget);
43
44/** @internal
45 *
46 * @brief Convenience function template that tests if a value is in a list.
47 *
48 * @param first The value
49 * @param t The list
50 *
51 * Usage:
52 * @snippet testhelper.cpp isInUsage
53 *
54 * @returns <tt>true</tt> if “value” is in “list”. <tt>false</tt> otherwise. */
55template<typename First, typename... T>
56bool isIn(First &&first, T &&...t)
57{
58 // Solution as proposed by Nikos C. in https://stackoverflow.com/a/15181949
59 return ((first == t) || ...);
60}
61
62[[nodiscard]] qreal standardWheelStepCount(QWheelEvent *event);
63
64[[nodiscard]] QImage transparencyBackground(qreal devicePixelRatioF);
65
66/** @internal
67 *
68 * @brief Two-dimensional array */
69template<typename T>
70class Array2D
71{
72public:
73 /** @brief Constructor.
74 *
75 * Constructs an array with the size 0 × 0. */
76 Array2D()
77 : m_iCount(0)
78 , m_jCount(0)
79 {
80 }
81
82 /** @brief Constructor.
83 *
84 * @param iCount size (first dimension)
85 * @param jCount size (second dimension)
86 *
87 * The elements are initialized with default-constructed values. */
88 Array2D(QListSizeType iCount, QListSizeType jCount)
89 : m_iCount(iCount)
90 , m_jCount(jCount)
91 {
92 if (m_iCount < 0) {
93 m_iCount = 0;
94 }
95 if (m_jCount < 0) {
96 m_jCount = 0;
97 }
98 const auto elementCount = m_iCount * m_jCount;
99 m_data.reserve(elementCount);
100 for (QListSizeType i = 0; i < elementCount; ++i) {
101 m_data.append(T());
102 }
103 }
104
105 /** @brief Constructor.
106 *
107 * @param iCount size (first dimension)
108 * @param jCount size (second dimension)
109 * @param init Initial values. Excess elements are ignored. Missing
110 * elements are initialized with default-constructed values. */
111 Array2D(QListSizeType iCount, QListSizeType jCount, QList<T> init)
112 : m_iCount(iCount)
113 , m_jCount(jCount)
114 {
115 if (m_iCount < 0) {
116 m_iCount = 0;
117 }
118 if (m_jCount < 0) {
119 m_jCount = 0;
120 }
121 const auto elementCount = m_iCount * m_jCount;
122 m_data.reserve(elementCount);
123 for (QListSizeType i = 0; i < elementCount; ++i) {
124 if (i < init.count()) {
125 m_data.append(init.value(i));
126 } else {
127 m_data.append(T());
128 }
129 }
130 }
131
132 /** @brief Set value at a given index.
133 *
134 * @param i index (first dimension)
135 * @param j index (second dimension)
136 * @param value value to set */
137 void setValue(QListSizeType i, QListSizeType j, const T &value)
138 {
139 if (isInRange<QListSizeType>(0, i, m_iCount - 1) //
140 && isInRange<QListSizeType>(0, j, m_jCount - 1) //
141 ) {
142 m_data[i + m_iCount * j] = value;
143 }
144 }
145
146 /** @brief Get value at a given index.
147 *
148 * @param i index (first dimension)
149 * @param j index (second dimension)
150 * @returns If the indices are valid, the value at the given indeces.
151 * A default-constructed value otherwise. */
152 T value(QListSizeType i, QListSizeType j) const
153 {
154 if (isInRange<QListSizeType>(0, i, m_iCount - 1) //
155 && isInRange<QListSizeType>(0, j, m_jCount - 1) //
156 ) {
157 return m_data[i + m_iCount * j];
158 }
159 return T(); // Default value if indices are out of bounds
160 }
161
162 /** @brief Size of the first dimension.
163 *
164 * @returns Size of the first dimension. */
165 QListSizeType iCount() const
166 {
167 return m_iCount;
168 }
169
170 /** @brief Size of the second dimension.
171 *
172 * @returns Size of the second dimension. */
173 QListSizeType jCount() const
174 {
175 return m_jCount;
176 }
177
178private:
179 /** @brief Internal storage of the elements. */
180 QList<T> m_data;
181 /** @brief Internal storage of @ref iCount(). */
182 QListSizeType m_iCount;
183 /** @brief Internal storage of @ref jCount(). */
184 QListSizeType m_jCount;
185};
186
187/** @internal
188 *
189 * @brief Force processing of events in a delayed fashion.
190 *
191 * When there is no running event loop (like in unit tests or in tools
192 * like the screenshot generator), some parts of the asynchronous API
193 * of this library does not work. Calling this function fixes this by
194 * forcing the processing of pending events, but with some delay
195 * in-between, so that maybe existing parallel threads have also
196 * a chance to terminate their work.
197 *
198 * @param msecWaitInitially Delay before starting event processing.
199 * @param msecWaitBetweenEventLoopPasses Delay before each pass through
200 * through the pending events.
201 * @param numberEventLoopPasses Number of passes through the pending events.
202 *
203 * @internal
204 *
205 * @note This is declared as template to prevent that this code is compiled
206 * into the library itself, which does <em>not</em> actually use it itself,
207 * but includes this header file. */
208template<typename T = void>
209void delayedEventProcessing(unsigned long msecWaitInitially = 50, unsigned long msecWaitBetweenEventLoopPasses = 50, int numberEventLoopPasses = 3)
210{
211 // Some OSes might round the sleep time up to 15 ms. We do it ourself
212 // here to make the behaviour a little bit more predictable.
213 msecWaitInitially = qMax<unsigned long>( //
214 msecWaitInitially, //
215 15);
216 msecWaitBetweenEventLoopPasses = //
217 qMax<unsigned long>(msecWaitBetweenEventLoopPasses, 15);
218
219 QThread::msleep(msecWaitInitially);
220 // Hopefully, now the render function has terminated…
221 for (int i = 0; i < numberEventLoopPasses; ++i) {
222 // Wait again (apparently, threaded event processing needs some time…)
223 QThread::msleep(msecWaitBetweenEventLoopPasses);
225 }
226}
227
228[[nodiscard]] Array2D<QColor> wcsBasicColors(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace);
229
230[[nodiscard]] QIcon qIconFromTheme(const QStringList &names, const QString &fallback, ColorSchemeType type);
231
232[[nodiscard]] QPair<QString, QString> getPrefixSuffix(const QString &formatString);
233
234/** @brief The full-qualified C++ identifier as QString.
235 *
236 * This can be useful for debugging purposes.
237 *
238 * @tparam T The enumeration.
239 *
240 * @pre The enumeration type is declared with
241 * Q_ENUM or Q_ENUM_NS.
242 *
243 * @returns The full-qualified C++ identifier as QString. */
244template<typename T>
246{
247 const auto myMeta = QMetaEnum::fromType<T>();
248 const auto scope = QString::fromUtf8(myMeta.scope());
249 const auto name = QString::fromUtf8(myMeta.name());
250 return QStringLiteral("%1::%2").arg(scope, name);
251}
252
253/** @brief The full-qualified C++ identifier as QString.
254 *
255 * This can be useful for debugging purposes.
256 *
257 * @tparam T The enumeration type. Can usually be omitted.
258 *
259 * @param enumerator An enumerator.
260 *
261 * @pre The enumeration type of the enumerator is declared with
262 * Q_ENUM or Q_ENUM_NS.
263 *
264 * @returns The full-qualified C++ identifier as QString, followed by the
265 * underlying integer value in parenthesis. If the enumerator does not
266 * exist (for example because you have done a static_cast of an invalid
267 * integer to the enum class), an empty String is returned instead. If
268 * the enumerator has synonyms (that means, there exist other enumerators
269 * that share the same integer with the current enumerator), all synonym
270 * enumerators are returned.
271 *
272 * @sa @ref enumeratorToString() */
273template<typename T>
274[[nodiscard]] QString enumeratorToFullString(const T &enumerator)
275{
276 const auto value = static_cast<int>(enumerator);
277 const auto myMeta = QMetaEnum::fromType<T>();
278
279 // QMetaEnum::valueToKeys (identifier with a final s) returns all existing
280 // (synonym) keys for a given value. But it also returns happily
281 // fantasy strings for non-existing values. Therefore, we have check
282 // first with QMetaEnum::valueToKeys (identifier with a final s) which
283 // does only return one single key for each value, but is guaranteed to
284 // return nullptr if the value has no key.
285 if (!myMeta.valueToKey(value)) {
286 return QString();
287 }
288
289 const auto scope = QString::fromUtf8(myMeta.scope());
290 const auto name = QString::fromUtf8(myMeta.name());
291 const auto keys = QString::fromUtf8(myMeta.valueToKeys(value));
292 return QStringLiteral("%1::%2::%3(%4)").arg(scope, name, keys).arg(value);
293}
294
295/** @brief The C++ identifier as QString.
296 *
297 * This can be useful for debugging purposes.
298 *
299 * @tparam T The enumeration type. Can usually be omitted.
300 *
301 * @param enumerator An enumerator.
302 *
303 * @pre The enumeration type of the enumerator is declared with
304 * Q_ENUM or Q_ENUM_NS.
305 *
306 * @returns The C++ identifier as QString, followed by the
307 * underlying integer value in parenthesis. If the enumerator does not
308 * exist (for example because you have done a static_cast of an invalid
309 * integer to the enum class), an empty String is returned instead. If
310 * the enumerator has synonyms (that means, there exist other enumerators
311 * that share the same integer with the current enumerator), all synonym
312 * enumerators are returned.
313 *
314 * @sa @ref enumeratorToFullString() */
315template<typename T>
316[[nodiscard]] QString enumeratorToString(const T &enumerator)
317{
318 const auto value = static_cast<int>(enumerator);
319 const auto myMeta = QMetaEnum::fromType<T>();
320
321 // QMetaEnum::valueToKeys (identifier with a final s) returns all existing
322 // (synonym) keys for a given value. But it also returns happily
323 // fantasy strings for non-existing values. Therefore, we have check
324 // first with QMetaEnum::valueToKeys (identifier with a final s) which
325 // does only return one single key for each value, but is guaranteed to
326 // return nullptr if the value has no key.
327 if (!myMeta.valueToKey(value)) {
328 return QString();
329 }
330
331 const auto keys = QString::fromUtf8(myMeta.valueToKeys(value));
332 return QStringLiteral("%1(%2)").arg(keys).arg(value);
333}
334
335} // namespace PerceptualColor
336
337#endif // HELPER_H
The namespace of this library.
QString enumerationToFullString()
The full-qualified C++ identifier as QString.
Definition helper.h:245
QString enumeratorToString(const T &enumerator)
The C++ identifier as QString.
Definition helper.h:316
QString enumeratorToFullString(const T &enumerator)
The full-qualified C++ identifier as QString.
Definition helper.h:274
ColorSchemeType
Represents the appearance of a theme.
Definition helper.h:33
Array2D< QColor > wcsBasicColors(const QSharedPointer< PerceptualColor::RgbColorSpace > &colorSpace)
Palette derived from the basic colors as by WCS (World color survey).
Definition helper.cpp:459
QCA_EXPORT void init()
void processEvents(QEventLoop::ProcessEventsFlags flags)
void append(QList< T > &&value)
void reserve(qsizetype size)
QString fromUtf8(QByteArrayView str)
void msleep(unsigned long msecs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:36 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.