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

KDE's Doxygen guidelines are available online.