Perceptual Color

colordialog_p.h
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4#ifndef COLORDIALOG_P_H
5#define COLORDIALOG_P_H
6
7// Include the header of the public class of this private implementation.
8#include "colordialog.h"
9
10#include "constpropagatingrawpointer.h"
11#include "genericcolor.h"
12#include "helper.h"
13#include "helperconversion.h"
14#include "helperqttypes.h"
15#include "languagechangeeventfilter.h"
16#include "perceptualsettings.h"
17#include "rgbcolor.h"
18#include <lcms2.h>
19#include <optional>
20#include <qbytearray.h>
21#include <qcolor.h>
22#include <qglobal.h>
23#include <qhash.h>
24#include <qobject.h>
25#include <qpointer.h>
26#include <qsharedpointer.h>
27#include <qstring.h>
28#include <qstringliteral.h>
29class QAction;
30class QComboBox;
32class QDoubleSpinBox;
33class QGroupBox;
34class QHBoxLayout;
35class QLabel;
36class QLineEdit;
37class QPushButton;
38class QShortcut;
39class QStackedLayout;
40class QTabWidget;
41class QToolButton;
42class QWidget;
43
44#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
45#include <qtmetamacros.h>
46#else
47#include <qobjectdefs.h>
48#endif
49
50namespace PerceptualColor
51{
53class ColorPatch;
54class GradientSlider;
55class MultiSpinBox;
56class SwatchBook;
57class RgbColorSpace;
59
60/** @internal
61 *
62 * @brief Private implementation within the <em>Pointer to
63 * implementation</em> idiom */
64class ColorDialogPrivate final : public QObject
65{
67public:
68 explicit ColorDialogPrivate(ColorDialog *backLink);
69 /** @brief Default destructor
70 *
71 * The destructor is non-<tt>virtual</tt> because
72 * the class as a whole is <tt>final</tt>. */
73 virtual ~ColorDialogPrivate() noexcept override = default;
74
75 /** @brief Horizontal count of swatches for history. */
76 static constexpr QListSizeType historyHSwatchCount = 10;
77 /** @brief Vertical count of swatches for history. */
78 static constexpr QListSizeType historyVSwatchCount = 4;
79 /** @brief Total count of swatches for history. */
80 static constexpr QListSizeType historySwatchCount = //
81 historyHSwatchCount * historyVSwatchCount;
82
83 /** @brief Horizontal count of swatches for custom colors. */
84 static constexpr QListSizeType customColorsHSwatchCount = 9;
85 /** @brief Vertical count of swatches for custom colors. */
86 static constexpr QListSizeType customColorsVSwatchCount = 4;
87 /** @brief Total count of swatches for custom colors. */
88 static constexpr QListSizeType customColorsSwatchCount = //
89 historyHSwatchCount * historyVSwatchCount;
90
91 /** @brief @ref GradientSlider widget for the alpha channel. */
92 QPointer<GradientSlider> m_alphaGradientSlider;
93 /** @brief Pointer to the QLabel for the alpha value.
94 *
95 * We store this in a pointer to allow toggle the visibility later. */
96 QPointer<QLabel> m_alphaLabel;
97 /** @brief Spin box for the alpha channel.
98 *
99 * This spin box shows always the value of @ref m_alphaGradientSlider.
100 *
101 * @note It’s value is not set directly, but is updated via signals from
102 * @ref m_alphaGradientSlider. Do not use it directly! */
103 QPointer<QDoubleSpinBox> m_alphaSpinBox;
104 /** @brief Pointer to the QButtonBox of this dialog.
105 *
106 * We store this in a pointer
107 * to allow toggle the visibility later. */
108 QPointer<QDialogButtonBox> m_buttonBox;
109 /** @brief Pointer to the “Cancel” button of @ref m_buttonBox. */
110 QPointer<QPushButton> m_buttonCancel;
111 /** @brief Pointer to the “Ok” button of @ref m_buttonBox. */
112 QPointer<QPushButton> m_buttonOK;
113 /** @brief Pointer to the @ref ChromaHueDiagram. */
114 QPointer<ChromaHueDiagram> m_chromaHueDiagram;
115 /** @brief Pointer to the @ref MultiSpinBox for CIELCH. */
116 QPointer<MultiSpinBox> m_cielchD50SpinBox;
117 /** @brief Pointer to the gamut action for @ref m_cielchD50SpinBox. */
118 QPointer<QAction> m_cielchD50SpinBoxGamutAction;
119 /** @brief Pointer to the label for @ref m_cielchD50SpinBox. */
120 QPointer<QLabel> m_cielchD50SpinBoxLabel;
121 /** @brief Pointer to the @ref ColorPatch widget. */
122 QPointer<ColorPatch> m_colorPatch;
123 /** @brief Holds the currently used icon theme.
124 *
125 * Initially this is set to an arbitrary value. Once @ref reloadIcons() has
126 * been called, it has an actual value. */
127 ColorSchemeType m_currentIconThemeType = ColorSchemeType::Light;
128 /** @brief Current color without alpha information
129 *
130 * Holds the color in absolutely defined color models.
131 *
132 * @note This value is considered in-gamut (even thought @ref RgbColor
133 * might tell different because of rounding errors).
134 *
135 * @sa @ref ColorDialog::currentColor()
136 * @sa @ref m_currentOpaqueColorRgb */
137 QHash<ColorModel, GenericColor> m_currentOpaqueColorAbs;
138 /** @brief Current color without alpha information
139 *
140 * Holds the color in the RGB color model and derived color models.
141 *
142 * @note This value is in-gamut by definition.
143 *
144 * @sa @ref ColorDialog::currentColor()
145 * @sa @ref m_currentOpaqueColorAbs */
146 RgbColor m_currentOpaqueColorRgb;
147 /** @brief If @ref q_pointer has ever been shown. */
148 bool everShown = false;
149 /** @brief Pointer to the @ref MultiSpinBox for HSL. */
150 QPointer<MultiSpinBox> m_hslSpinBox;
151 /** @brief Pointer to the label for @ref m_hslSpinBox. */
152 QPointer<QLabel> m_hslSpinBoxLabel;
153 /** @brief Pointer to the @ref MultiSpinBox for HSV. */
154 QPointer<MultiSpinBox> m_hsvSpinBox;
155 /** @brief Pointer to the label for @ref m_hsvSpinBox. */
156 QPointer<QLabel> m_hsvSpinBoxLabel;
157 /** @brief Pointer to the @ref MultiSpinBox for HWB. */
158 QPointer<MultiSpinBox> m_hwbSpinBox;
159 /** @brief Pointer to the label for @ref m_hwbSpinBox. */
160 QPointer<QLabel> m_hwbSpinBoxLabel;
161 /** @brief Shortcut to show the tab with @ref m_hueFirstWrapperWidget. */
162 QPointer<QShortcut> m_hueFirstTabShortcut;
163 /** @brief Pointer to the QWidget wrapper that contains
164 * @ref m_wheelColorPicker. */
165 QPointer<QWidget> m_hueFirstWrapperWidget;
166 /** @brief Holds whether currently a color change is ongoing, or not.
167 *
168 * Used to avoid infinite recursions when updating the different widgets
169 * within this dialog.
170 * @sa @ref setCurrentOpaqueColor() */
171 bool m_isColorChangeInProgress = false;
172 /** @brief Holds whether the current text of @ref m_rgbLineEdit differs
173 * from the value in @ref m_currentOpaqueColorRgb.
174 * @sa @ref readRgbHexValues
175 * @sa @ref updateRgbHexButBlockSignals */
176 bool m_isDirtyRgbLineEdit = false;
177 /** @brief An event filter used for some child widgets. */
178 LanguageChangeEventFilter m_languageChangeEventFilter;
179 /** @brief Internal storage for property
180 * @ref ColorDialog::layoutDimensions */
181 PerceptualColor::ColorDialog::DialogLayoutDimensions m_layoutDimensions =
182 //! [layoutDimensionsDefaultValue]
183 ColorDialog::DialogLayoutDimensions::Collapsed
184 //! [layoutDimensionsDefaultValue]
185 ;
186 /** @brief The <em>effective</em> layout dimensions.
187 *
188 * The property @ref ColorDialog::layoutDimensions has a value
189 * @ref ColorDialog::DialogLayoutDimensions::ScreenSizeDependent.
190 * <em>This</em> variable holds whatever <em>effectively</em>
191 * is applied. So it can only have the values
192 * @ref ColorDialog::DialogLayoutDimensions::Collapsed or
193 * @ref ColorDialog::DialogLayoutDimensions::Expanded. */
194 PerceptualColor::ColorDialog::DialogLayoutDimensions m_layoutDimensionsEffective = m_layoutDimensions;
195 /** @brief Shortcut to show the tab with @ref m_lightnessFirstWrapperWidget. */
196 QPointer<QShortcut> m_lightnessFirstTabShortcut;
197 /** @brief Pointer to the QWidget wrapper that contains
198 * @ref m_lchLightnessSelector and @ref m_chromaHueDiagram. */
199 QPointer<QWidget> m_lightnessFirstWrapperWidget;
200 /** @brief Pointer to the @ref GradientSlider for LCH lightness. */
201 QPointer<GradientSlider> m_lchLightnessSelector;
202 /** @brief Holds the receiver slot (if any) to be disconnected
203 * automatically after closing the dialog.
204 *
205 * Its value is only meaningful if
206 * @ref m_receiverToBeDisconnected is not null.
207 * @sa @ref m_receiverToBeDisconnected
208 * @sa @ref ColorDialog::open() */
209 QByteArray m_memberToBeDisconnected;
210 /** @brief String that is used as separator between two sections
211 * within a @ref MultiSpinBox.
212 *
213 * This string is introduced <em>twice</em> between two sections
214 * within a @ref MultiSpinBox. */
215 static inline const QString m_multispinboxSectionSeparator = QStringLiteral(u" ");
216 /** @brief Shortcut to show the tab with @ref m_numericalWidget. */
217 QPointer<QShortcut> m_numericalTabShortcut;
218 /** @brief Pointer to the widget that holds the numeric color
219 * representation. */
220 QPointer<QWidget> m_numericalWidget;
221 /** @brief Pointer to the @ref MultiSpinBox for Oklch. */
222 QPointer<MultiSpinBox> m_oklchSpinBox;
223 /** @brief Pointer to the gamut action for @ref m_oklchSpinBox. */
224 QPointer<QAction> m_oklchSpinBoxGamutAction;
225 /** @brief Pointer to the label for @ref m_oklchSpinBox. */
226 QPointer<QLabel> m_oklchSpinBoxLabel;
227 /** @brief Pointer to the basic colors widget. */
228 QPointer<PerceptualColor::SwatchBook> m_swatchBookBasicColors;
229 /** @brief Pointer to the basic colors widget. */
230 QPointer<PerceptualColor::SwatchBook> m_swatchBookCustomColors;
231 /** @brief Pointer to the history widget. */
232 QPointer<PerceptualColor::SwatchBook> m_swatchBookHistory;
233 /** @brief The selector widget to choose which swatch book to show.
234 *
235 * @sa @ref m_swatchBookStack */
236 QPointer<QComboBox> m_swatchBookSelector;
237 /** @brief Stack containing the various @ref SwatchBook widgets.
238 *
239 * @sa @ref m_swatchBookSelector */
240 QPointer<QStackedLayout> m_swatchBookStack;
241 /** @brief Shortcut to show the tab with @ref m_swatchBookWrapperWidget. */
242 QPointer<QShortcut> m_swatchBookTabShortcut;
243 /** @brief Pointer to the QWidget wrapper that contains
244 * the swatch books. */
245 QPointer<QWidget> m_swatchBookWrapperWidget;
246 /** @brief Holds the receiver object (if any) to be disconnected
247 * automatically after closing the dialog.
248 *
249 * @sa @ref m_memberToBeDisconnected
250 * @sa @ref ColorDialog::open() */
251 QPointer<QObject> m_receiverToBeDisconnected;
252 /** @brief Internal storage for property @ref ColorDialog::options */
253 ColorDialog::ColorDialogOptions m_options;
254 /** @brief Pointer to the RgbColorSpace object. */
255 QSharedPointer<RgbColorSpace> m_rgbColorSpace;
256 /** @brief Group box that contains all RGB widgets and all widget for
257 * color spaces that are defined with RGB as base (HSV, Hex…). */
258 QPointer<QGroupBox> m_rgbGroupBox;
259 /** @brief Pointer to the QLineEdit that represents the hexadecimal
260 * RGB value. */
261 QPointer<QLineEdit> m_rgbLineEdit;
262 /** @brief Pointer to the label for @ref m_rgbLineEdit. */
263 QPointer<QLabel> m_rgbLineEditLabel;
264 /** @brief Pointer to the @ref MultiSpinBox for RGB. */
265 QPointer<MultiSpinBox> m_rgbSpinBox;
266 /** @brief Pointer to the label for @ref m_rgbSpinBox. */
267 QPointer<QLabel> m_rgbSpinBoxLabel;
268 /** @brief Internal storage for @ref ColorDialog::selectedColor(). */
269 QColor m_selectedColor;
270 /** @brief Layout that holds the graphical and numeric selectors. */
271 QPointer<QHBoxLayout> m_selectorLayout;
272 /** @brief Access to the @ref Settings singleton. */
273 PerceptualSettings &m_settings = PerceptualSettings::instance();
274 /** @brief Button that allows to pick with the mouse a color somewhere
275 * from the screen. */
276 QPointer<QToolButton> m_screenColorPickerButton;
277 /** @brief A row with two columns within a table in Qt’s rich text
278 * formatting.
279 *
280 * To use it, call QString::arg() twice: Once with the content of the
281 * first column and once with the content of the second column. */
282 const QString tableRow = QStringLiteral(u"<tr><td>%1</td><td>%2</td></tr>");
283 /** @brief Table assigning to each tab a value for the @ref Settings.
284 *
285 * This helps to convert from QString values stored in @ref Settings
286 * to the actual tab widgets and vice versa. */
287 QHash<QPointer<QWidget> *, QString> m_tabTable;
288 /** @brief Pointer to the tab widget. */
289 QPointer<QTabWidget> m_tabWidget;
290 /** @brief @ref m_wcsBasicColors for @ref m_rgbColorSpace. */
291 Swatches m_wcsBasicColors;
292 /** @brief A default color within @ref m_wcsBasicColors.
293 *
294 * Choosing the blue tone (no tint, no shade). Arguments in favor:
295 *
296 * - Blue seems to be harmonious and integrate well in many designs.
297 * - The blue color is quite chromatic, giving a vivid impression.
298 * - Blue does not “screem” like red.
299 * - Blue is exactly at the middle of the swatch book.
300 * - The tone (no tint, no shade) is exactly at the middle of the
301 * swatch book. */
302 QColor m_wcsBasicDefaultColor;
303 /** @brief Pointer to the @ref WheelColorPicker widget. */
304 QPointer<WheelColorPicker> m_wheelColorPicker;
305
306 /** @brief Number of decimals to for most values.
307 *
308 * @sa @ref okdecimals */
309 static constexpr quint8 decimals = 0;
310 /** @brief Number of decimals to use for the Oklab/Oklch values
311 * L, C, a, b (but not for h!).
312 *
313 * @sa @ref decimals */
314 static constexpr quint8 okdecimals = decimals + 2;
315
316 void applyLayoutDimensions();
317 [[nodiscard]] QColor defaultColor() const;
318 void initialize(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace);
319 [[nodiscard]] QWidget *initializeNumericPage();
320 void initializeScreenColorPicker();
321 void loadCustomColorsFromSettingsToSwatchBook();
322 void loadHistoryFromSettingsToSwatchBook();
323 [[nodiscard]] QString translateColorModel(cmsColorSpaceSignature model);
324
325public Q_SLOTS:
326 void readChromaHueDiagramValue();
327 void readColorPatchValue();
328 void readHslNumericValues();
329 void readHsvNumericValues();
330 void readHwbNumericValues();
331 void readLchNumericValues();
332 void readLightnessValue();
333 void readOklchNumericValues();
334 void readRgbHexValues();
335 void readRgbNumericValues();
336 void readSwatchBookBasicColorsValue();
337 void readSwatchBookCustomColorsValue();
338 void readSwatchBookHistoryValue();
339 void readWheelColorPickerValues();
340 void reloadIcons();
341 void retranslateUi();
342 void saveCurrentTab();
343 void setCurrentOpaqueColor(const QHash<PerceptualColor::ColorModel, PerceptualColor::GenericColor> &abs, QWidget *const ignoreWidget);
344 void setCurrentOpaqueColor(const PerceptualColor::RgbColor &rgb, QWidget *const ignoreWidget);
345 void setCurrentOpaqueColor(const QHash<PerceptualColor::ColorModel, PerceptualColor::GenericColor> &abs,
346 const PerceptualColor::RgbColor &rgb,
347 QWidget *const ignoreWidget);
348 void updateColorPatch();
349 void updateLchButBlockSignals();
350 void updateOklchButBlockSignals();
351 void updateRgbHexButBlockSignals();
352
353private:
354 Q_DISABLE_COPY(ColorDialogPrivate)
355
356 /** @brief Pointer to the object from which <em>this</em> object
357 * is the private implementation. */
358 ConstPropagatingRawPointer<ColorDialog> q_pointer;
359};
360
361} // namespace PerceptualColor
362
363#endif // COLORDIALOG_P_H
A widget for selecting chroma and hue in LCH color space.
A color display widget.
Definition colorpatch.h:69
A slider who’s groove displays an LCH color gradient.
A spin box that can hold multiple sections (each with its own value) at the same time.
Complete wheel-based color picker widget.
The namespace of this library.
Array2D< QColor > Swatches
Swatches organized in a grid.
Definition helper.h:262
ColorSchemeType
Represents the appearance of a theme.
Definition helper.h:41
ColorModel
Identifiers for color models.
QObject(QObject *parent)
Q_OBJECTQ_OBJECT
Q_SLOTSQ_SLOTS
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 12:03:13 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.