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 T value() const;
45
46 void setValue(const T &newValue);
47
48private:
49 // Prevent copy and assignment operations to force that only references
50 // to the instance are possible.
51 Setting(const Setting &) = delete;
52 Setting &operator=(const Setting &) = delete;
53
54 /** @brief Internal storage for the value. */
55 T m_value = T();
56
57 void updateFromQSettings();
58
59 /** @internal @brief Only for unit tests. */
60 friend class TestSetting;
61};
62
63/** @brief Constructor.
64 *
65 * @param key <tt>QSettings</tt> key for the value.
66 * For maximum portability:
67 * - No upper case should ever be used.
68 * (Some systems, like the INI that we are using, are case-insensitive.
69 * And even if we always use INI, having both capital and small letters
70 * is error-prone because typos are not checked by the compiler.)
71 * - Only the letters a-z should be used.
72 * (Also, some characters like the backslash are not allowed on some
73 * platforms.)
74 * - “group/key”: Each key has exactly one group. Don't use subgroups.
75 * Use the class name as group name.
76 * (This makes the settings file well readable for humans. Missing
77 * groups are confusing because the system generates a “General”
78 * group which is not easy to understand. And using class identifiers
79 * helps to understand the structure of the settings file.)
80 * - In C++, use “const” variables to define key strings, instead of
81 * manually typing the key strings.
82 * (This avoids typing errors.)
83 * @param settings Corresponding @ref Settings object. This object must
84 * stay available during the live-time of this object.
85 * @param parent The parent object (if any).
86 *
87 * @warning You must not create more than one instance with the same
88 * combination between key and @ref Settings object. This would result
89 * in undefined behaviour. (Probably some values would be out-of-sync.) */
90template<typename T>
91Setting<T>::Setting(const QString &key, Settings *settings, QObject *parent)
92 : SettingBase(key, settings, parent)
93{
94 // QSettings seems to use indirectly QMetaType::load() which requires
95 // to register all custom types as QMetaType.
96 qRegisterMetaType<T>();
97#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
98 // Also stream operators are required.
99 qRegisterMetaTypeStreamOperators<T>();
100#endif
101
102 // Initialize the internal value:
103 updateFromQSettings();
104
105 // Make sure further updates are processed.
106 connect(settings, //
107 &Settings::updatedAfterFileChange, //
108 this, //
109 &PerceptualColor::Setting<T>::updateFromQSettings //
110 );
111}
112
113/** @brief Destructor. */
114template<typename T>
115Setting<T>::~Setting()
116{
117}
118
119/** @brief Updates the value to the corresponding value
120 * from @ref underlyingQSettings().
121 *
122 * Only reads from @ref underlyingQSettings() and does never write back
123 * to @ref underlyingQSettings(). */
124template<typename T>
125void Setting<T>::updateFromQSettings()
126{
127 // WARNING: Do not use the setter, as this may trigger
128 // unnecessary file writes even if the property hasn't changed. If
129 // another instance tries to write to the same file at the same time,
130 // it could cause a deadlock since our code would perform two file
131 // access operations. Another process could potentially lock the file
132 // just in between the two writes, leading to a deadlock. To prevent
133 // such issues, our code only reads from QSettings and never writes
134 // back directly or indirectly. Instead, we modify the property's
135 // internal storage directly and emit the notify signal if necessary.
136
137 const QVariant newValueVariant = underlyingQSettings()->value(m_key);
138 const T newValue = newValueVariant.value<T>();
139 if (newValue != m_value) {
140 m_value = newValue;
141 Q_EMIT valueChanged();
142 }
143}
144
145/** @brief Getter.
146 * @returns the value. */
147template<typename T>
148T Setting<T>::value() const
149{
150 return m_value;
151}
152
153/** @brief Setter.
154 *
155 * @param newValue The new value. */
156template<typename T>
157void Setting<T>::setValue(const T &newValue)
158{
159 if (newValue != m_value) {
160 m_value = newValue;
161 const auto newVariant = QVariant::fromValue<T>(m_value);
162 underlyingQSettings()->setValue(m_key, newVariant);
163 Q_EMIT valueChanged();
164 }
165}
166
167} // namespace PerceptualColor
168
169#endif // SETTING_H
The namespace of this library.
QObject * parent() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:51:26 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.