Perceptual Color

setting.h
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4#ifndef SETTING_H
5#define SETTING_H
6
7#include "settingbase.h"
8#include "settings.h"
9#include <qcolor.h>
10#include <qdebug.h>
11#include <qfilesystemwatcher.h>
12#include <qglobal.h>
13#include <qlist.h>
14#include <qmetatype.h>
15#include <qobject.h>
16#include <qsettings.h>
17#include <qstring.h>
18#include <qstringliteral.h>
19
20#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
21#include <qtmetamacros.h>
22#else
23#include <qobjectdefs.h>
24#endif
25
26namespace PerceptualColor
27{
28
29/** @internal
30 *
31 * @brief A single setting within @ref Settings.
32 *
33 * @tparam T Type of the setting value. The type must qualify for
34 * registration as QMetaType and also provide a stream
35 * operator. @ref PerceptualSettings::ColorList and many
36 * build-in types qualify.) */
37template<typename T>
38class Setting final : public SettingBase
39{
40public:
41 Setting(const QString &key, Settings *settings, QObject *parent = nullptr);
42 virtual ~Setting() override;
43
44 // cppcheck-suppress returnByReference // false positive
45 T value() const;
46
47 void setValue(const T &newValue);
48
49private:
50 // Prevent copy and assignment operations to force that only references
51 // to the instance are possible.
52 Setting(const Setting &) = delete;
53 Setting &operator=(const Setting &) = delete;
54
55 /** @brief Internal storage for the value. */
56 T m_value = T();
57
58 void updateFromQSettings();
59
60 /** @internal @brief Only for unit tests. */
61 friend class TestSetting;
62};
63
64/** @brief Constructor.
65 *
66 * @param key <tt>QSettings</tt> key for the value.
67 * For maximum portability:
68 * - No upper case should ever be used.
69 * (Some systems, like the INI that we are using, are case-insensitive.
70 * And even if we always use INI, having both capital and small letters
71 * is error-prone because typos are not checked by the compiler.)
72 * - Only the letters a-z should be used.
73 * (Also, some characters like the backslash are not allowed on some
74 * platforms.)
75 * - “group/key”: Each key has exactly one group. Don't use subgroups.
76 * Use the class name as group name.
77 * (This makes the settings file well readable for humans. Missing
78 * groups are confusing because the system generates a “General”
79 * group which is not easy to understand. And using class identifiers
80 * helps to understand the structure of the settings file.)
81 * - In C++, use “const” variables to define key strings, instead of
82 * manually typing the key strings.
83 * (This avoids typing errors.)
84 * @param settings Corresponding @ref Settings object. This object must
85 * stay available during the live-time of this object.
86 * @param parent The parent object (if any).
87 *
88 * @warning You must not create more than one instance with the same
89 * combination between key and @ref Settings object. This would result
90 * in undefined behaviour. (Probably some values would be out-of-sync.) */
91template<typename T>
92Setting<T>::Setting(const QString &key, Settings *settings, QObject *parent)
93 : SettingBase(key, settings, parent)
94{
95 // QSettings seems to use indirectly QMetaType::load() which requires
96 // to register all custom types as QMetaType.
97 qRegisterMetaType<T>();
98#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
99 // Also stream operators are required.
100 qRegisterMetaTypeStreamOperators<T>();
101#endif
102
103 // Initialize the internal value:
104 updateFromQSettings();
105
106 // Make sure further updates are processed.
107 connect(settings, //
108 &Settings::updatedAfterFileChange, //
109 this, //
110 &PerceptualColor::Setting<T>::updateFromQSettings //
111 );
112}
113
114/** @brief Destructor. */
115template<typename T>
116Setting<T>::~Setting()
117{
118}
119
120/** @brief Updates the value to the corresponding value
121 * from @ref underlyingQSettings().
122 *
123 * Only reads from @ref underlyingQSettings() and does never write back
124 * to @ref underlyingQSettings(). */
125template<typename T>
126void Setting<T>::updateFromQSettings()
127{
128 // WARNING: Do not use the setter, as this may trigger
129 // unnecessary file writes even if the property hasn't changed. If
130 // another instance tries to write to the same file at the same time,
131 // it could cause a deadlock since our code would perform two file
132 // access operations. Another process could potentially lock the file
133 // just in between the two writes, leading to a deadlock. To prevent
134 // such issues, our code only reads from QSettings and never writes
135 // back directly or indirectly. Instead, we modify the property's
136 // internal storage directly and emit the notify signal if necessary.
137
138 const QVariant newValueVariant = underlyingQSettings()->value(m_key);
139 const T newValue = newValueVariant.value<T>();
140 if (newValue != m_value) {
141 m_value = newValue;
142 Q_EMIT valueChanged();
143 }
144}
145
146/** @brief Getter.
147 * @returns the value. */
148template<typename T>
149T Setting<T>::value() const
150{
151 return m_value;
152}
153
154/** @brief Setter.
155 *
156 * @param newValue The new value. */
157template<typename T>
158void Setting<T>::setValue(const T &newValue)
159{
160 if (newValue != m_value) {
161 m_value = newValue;
162 const auto newVariant = QVariant::fromValue<T>(m_value);
163 underlyingQSettings()->setValue(m_key, newVariant);
164 Q_EMIT valueChanged();
165 }
166}
167
168} // namespace PerceptualColor
169
170#endif // SETTING_H
The namespace of this library.
QObject * parent() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
T value() const const
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.