Perceptual Color

colordialog.h
1// SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR MIT
3
4#ifndef COLORDIALOG_H
5#define COLORDIALOG_H
6
7#include "constpropagatinguniquepointer.h"
8#include "importexport.h"
9#include <qcolor.h>
10#include <qcolordialog.h>
11#include <qdialog.h>
12#include <qglobal.h>
13#include <qmetatype.h>
14#include <qnamespace.h>
15#include <qsharedpointer.h>
16#include <qstring.h>
17class QEvent;
18class QObject;
19class QShowEvent;
20class QWidget;
21
22#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
23#include <qtmetamacros.h>
24#else
25#include <qobjectdefs.h>
26#endif
27
28namespace PerceptualColor
29{
30class ColorDialogPrivate;
31
32class RgbColorSpace;
33
34/** @brief A perceptually uniform color picker dialog
35 *
36 * The color dialog’s function is to allow users to choose colors intuitively.
37 * For example, you might use this in a drawing program to allow the user to
38 * set the brush color.
39 *
40 * At difference to QColorDialog, this
41 * dialog’s graphical components are perceptually uniform and therefore more
42 * intuitive. It’s internally based on the LCH color model, which does reflect
43 * the human perception much better than RGB or its transforms like HSV. At
44 * the same time, this dialog does not require the user itself to know
45 * anything about LCH at all, because the graphical representations is
46 * intuitive.
47 *
48 * @image html ColorDialog.png "ColorDialog" width=500
49 * @image html ColorDialogTab1.png "ColorDialog" width=500
50 * @image html ColorDialogTab2.png "ColorDialog" width=500
51 *
52 * It is an @ref qdialogsourceompatibility "mostly source-compatible"
53 * replacement for QColorDialog. It also adds some extra functionality
54 * that is not available in QColorDialog.
55 *
56 * Just as with QColorDialog, the static functions provide a modal color
57 * dialog. The static getColor() function shows the dialog, and allows
58 * the user to specify a color:
59 *
60 * @snippet testcolordialog.cpp ColorDialog Get color
61 *
62 * The function can also be used to let
63 * users choose a color with a level of transparency: pass the alpha channel
64 * option as an additional argument:
65 *
66 * @snippet testcolordialog.cpp ColorDialog Get color with alpha channel
67 *
68 * @image html ColorDialogAlpha.png "ColorDialog with alpha channel" width=500
69 *
70 * More features:
71 *
72 * - A screen color picker is provided on many platforms.
73 *
74 * - For a non-modal dialog, use the normal constructors of this class.
75 *
76 * - The default window title is <em>Select Color</em>, and not the title of
77 * your application. It can of course be customized with
78 * <tt>QWidget::setWindowTitle()</tt>. The window title will
79 * <em>not</em> be updated on <tt>QEvent::LanguageChange</tt> events.
80 *
81 * - At difference to the usual native platform color dialog, <em>this</em>
82 * dialog can be resized. That makes sense, because it allows to see better
83 * the gamut image. Therefore, this dialog is by default bigger than the usual
84 * native platform color dialog. You can of course customize the dialog
85 * size with QWidget::resize() or force a more space-saving layout through
86 * the @ref layoutDimensions property.
87 *
88 * - The @ref ColorPatch that indicates the selected color is placed prominently
89 * at the top of the widget. That is also useful for touch screens as the
90 * @ref ColorPatch will not be hidden by the hand of the user when the user
91 * is touching the above color selection widgets.
92 *
93 * - This dialog uses icons. See @ref hidpisupport "High DPI support"
94 * about how to enable support for high-DPI icons.
95 *
96 * @note @anchor qdialogsourceompatibility The API of this class is mostly
97 * source-compatible to the API of QColorDialog. This is a list of
98 * incompatibilities:
99 * - The constructors and also @ref getColor() require a color space
100 * as argument.
101 * - As this dialog does not provide functionality for custom colors and
102 * standard color, the corresponding static functions of QColorDialog are
103 * not available in this class.
104 * - The option <tt>ColorDialogOption::DontUseNativeDialog</tt>
105 * will always remain <tt>false</tt> (even if set explicitly), because it’s
106 * just the point of this library to provide an own, non-native dialog.
107 * - While the enum declaration @ref ColorDialogOption itself is aliased
108 * here, this isn't possible for the enum values itself. Therefor, when
109 * working with @ref options, you cannot use <tt>ShowAlphaChannel</tt> but
110 * you have to use the fully qualified identifier (either
111 * <tt>PerceptualColor::ColorDialog::ColorDialogOption::ShowAlphaChannel</tt>
112 * or <tt>QColorDialog::ShowAlphaChannel</tt>, at your option.
113 * - Calling @ref setCurrentColor() with colors that
114 * are <em>not</em> <tt>QColor::Spec::Rgb</tt> will lead to an automatic
115 * conversion like QColorDialog does, but at difference to QColorDialog, it
116 * is done with more precision, therefor the resulting
117 * @ref currentColor() might be slightly different. The same is true
118 * for <tt>QColor::Spec::Rgb</tt> types with floating point precision:
119 * While QColorDialog would round to full integers, <em>this</em> dialog
120 * preserves the floating point precision.
121 * - When the default constructor is used, unlike QColorDialog, the default
122 * color is <em>not</em> guaranteed to be <tt>Qt::white</tt>.
123 *
124 * @internal
125 *
126 * @note In the QColorDialog API, the function <tt>QColorDialog::getRgba()</tt>
127 * has been deprecated during the Qt5 life cycle. This class does not provide
128 * source compatibility with obsolete functions of the QColorDialog API.
129 *
130 * @todo The @ref ColorDialogPrivate::m_lchLightnessSelector has a different
131 * scale than the @ref ColorDialogPrivate::m_chromaHueDiagram, but both are
132 * directly side-by-side in the dialog. Would it make sense to use the
133 * same scale for both?
134 *
135 * @todo Provide an option to enable labels for the
136 * <a href="https://www.handprint.com/HP/WCL/color12.html">warm-cool</a>
137 * contrast, maybe outside the wheel around the position 45° (warm) and
138 * 135° (cool).
139 *
140 * @todo In web design, CSS4 is often used. CSS4 also supports the
141 * <a href="https://www.w3.org/TR/css-color-4/#the-hsl-notation"> HSL</a> and
142 * the <a href="https://www.w3.org/TR/css-color-4/#the-hwb-notation">HBW</a>
143 * color space. We should do the same.
144 *
145 * @todo Provide <tt>setResizable(bool resizable)</tt>. Do not provide
146 * a property, because in the background this is setting <em>two</em>
147 * different values, which might be conflicting in the moment when read
148 * access is done. Code within ColorDialog to enable:
149 * <tt><br/>
150 * layout()->setSizeConstraint(QLayout::SetFixedSize);
151 * <br/>
152 * setSizeGripEnabled(false);
153 * <br/></tt>
154 * Document the performance impact: When resizing is allowed (which is the
155 * default), the user can make the window very big, which might increase
156 * the rendering time considerable.
157 *
158 * @todo Use <tt>QSignalBlocker</tt> instead of m_updateIsRunning in
159 * ColorDialog. Attention: The connection alpha-slider and alpha-spinbox
160 * is special and would not work anymore, which has to be fixed.
161 *
162 * @todo In KDE’s systemsettings, in the “Fonts” settings, there are small
163 * buttons showing a “i” symbol (for “information”) that has no functionality,
164 * but a tooltip for the widget at the left. Would this be also good for
165 * @ref ColorDialog?
166 *
167 * @todo Recently KDE merged something that displays tooltips and integrates a
168 * link that calls the <tt>QWhatsThis</tt> which has more information. Would
169 * this be helpful here also?
170 *
171 * @todo Since August 2021 <a href="https://phabricator.kde.org/T9460">the
172 * Breeze styling of spin boxes has changed</a>. Is or should be the
173 * new default horizontal alignment “center”?
174 *
175 * @todo Make sure that @ref ChromaHueDiagram always shows at least at the
176 * central physical pixel with an in-gamut color. Solution: Limit the range
177 * of the lightness selector? Or a better algorithm in @ref ChromaHueDiagram?
178 *
179 * @todo Provide (on demand) two patches, like Scribus also does: One for the
180 * old color (cannot be modified by the user) and another one for the new
181 * color (same behavior as the yet existing color patch). One could be
182 * named “before” and the other “after”. Or maybe make this configurable?
183 * And put an arrow between the patches, from “before” to “after”. (Be aware:
184 * RTL support necessary!)
185 *
186 * @section uireview Review of the user interface
187 *
188 * @todo The HLC widget accepts values that are out-of-gamut; the dialog
189 * shows the nearest in-gamut color during this moment. Also, the RGB Hex
190 * widget accepts intermediatly invalid values (for example, 4-digit-values);
191 * the dialog displays the last previously displayed valid color during
192 * this moment. Those two situations do not have consiteend behaviour,
193 * but there are no good alternatives. However: Would it make sense to
194 * keep this behaviour, but display in the @ref ColorPatch an empty
195 * value during editing an out-of-gamut HLC value or an invalid RGB Hex values?
196 *
197 * @todo For the tab widget, use rather icons instead of the text “hue-baded”
198 * and “lightness-based”!?
199 *
200 * @todo Provide more <tt>tooltip()</tt> help for widgets. For
201 * @ref WheelColorPicker and @ref ChromaLightnessDiagram, this
202 * help text could describe the keyboard controls and be integrated
203 * as default value in the class itself. For the other widgets, a
204 * help text could be defined here within <em>this</em> class,
205 * if appropriate.
206 *
207 * @todo Touch screen compatibility: In general: What would mean better
208 * support for touch-screen and convertible? More things to do? For example,
209 * A spin box can also be used on mobile phone (putting the numbers
210 * with on-screen keyboard). But the + and - button for increasing
211 * or decreasing the values might be too small. And mobile UI uses
212 * often wheels for this use case…
213 *
214 * @section additionalcolordialogfeatures Proposals for additional features
215 *
216 * @todo The QLineEdit for the hexadecimal RGB values should change lower-case
217 * letters on-the-fly (as-you-type) to upper-case letters. And: Maybe it
218 * could even be switched to @ref MultiSpinBox (but that would mean implement
219 * support for hexadecimal digits in @ref MultiSpinBox).
220 *
221 * @todo Accept <tt>F5</tt> and <tt>Ctrl+R</tt> just with the same
222 * functionality as the gamut button in the HCL @ref MultiSpinBox.
223 * But attention: If a library user <em>embeds</em> the dialog, he does
224 * not want his shortcuts to be intercepted!
225 *
226 * @todo It might be nice to support keyboard shortcuts for switching tabs
227 * like in browsers, which is a concept many users might be familiar to.
228 * Crtl+Tab to switch to the next tab in the list. Crtl+Shift+Tab to switch
229 * to the previous tab in the list.
230 *
231 * @todo Start with dialog with Qt::yellow. You get HLC 100° 98% 95.
232 * Push the apply button. Actual result: HLC 100° 98% 94 which has a slightly
233 * different chroma value. Expected result: Ideally there would be no rounding
234 * difference at all: Every once displayed value is always recognized as
235 * valid. When the color space conversion takes place, we could (and should)
236 * make this sure for HLC values. But then comes the also the rounding
237 * in @ref MultiSpinBox. Is there any solution?
238 *
239 * @todo If there is no alpha widget <em>and</em> the actual layout is
240 * expanded (either explicitly by @ref DialogLayoutDimensions::Expanded
241 * or implicitly by @ref DialogLayoutDimensions::ScreenSizeDependent on
242 * bigger screens) <em>than</em> it would make sense to move
243 * the @ref ColorDialogPrivate::m_buttonBox into the same column as
244 * the @ref ColorDialogPrivate::m_numericalWidget, namely <em>below</em>
245 * the @ref ColorDialogPrivate::m_numericalWidget. This saves screen
246 * space and does not confuse the user.
247 *
248 * @todo Provide an overloaded version of @ref open() that accepts
249 * arguments for new-style connect statements, making use of compiler
250 * checks.
251 *
252 * @todo Use the <em>actual</em> color profile of the monitor.
253 *
254 * @todo The LCh-hue (and so the graphical widgets) jumps forward and backward
255 * when changing RGB-based values (also HSV) when entering and leaving the gray
256 * axis, due to lack of hue information. Would it be an option to store the
257 * old hue to get a meaningful hue?
258 * Should it be only really for the gray axis, or allow a certain tolerance
259 * around the gray axis is necessary to make this work well - and if so,
260 * how much tolerance? Would it be useful to define a certain hue, for
261 * example 0°, as default hue for when no old hue is available but the
262 * new value is on the gray axis?
263 *
264 * @todo Custom layout management that has a specific height-per-width ratio
265 * considering the @ref ChromaHueDiagram and and @ref WheelColorPicker
266 * and <em>their</em> useful height-per-width ratio.
267 *
268 * @todo Support for other models like HSL (HSB is yet identical to HSV?),
269 * Munsell? With an option to enable or disable them? (NCS not, because
270 * it is not free…)
271 *
272 * @todo Would CMYK support make sense? Would it integrate intuitively into
273 * the user interface? If we would have CMYK support, we would have two
274 * different profiles (RGB and CMYK) active simultaniously. Which one is the
275 * profile that defines the available color space? Would this be intuitive?
276 * Also, when using two different profiles, it will be possible to have
277 * out-of-gamut colors in one of the profiles. How to represent this in the
278 * UI?
279 *
280 * @todo Provide more palettes in our swatch books? Include there a palette
281 * with QColorDialog’s standard colors as RGB values without
282 * a specific color space, with the name “Default”? Add
283 * <a href="https://www.w3.org/TR/css-color-4/#named-colors">CSS colors</a>
284 * as defined as sRGB values?
285 * <a href="https://en.wikipedia.org/wiki/Web_colors#Web-safe_colors">
286 * Web-safe colors</a> (but with another name, maybe “216 colors”)
287 * as bigger palette, without a specific color space – does
288 * not make sense, since we provide a perceptual color dialog,
289 * which allows things much beyond 216 colors, and 216 colors
290 * isn’t a useful standard anymore, and not a nice palette either.
291 *
292 * @todo Discrete widgets, that have
293 * a fixed (quite limited) number of fields to chose for the user?
294 */
296{
297 Q_OBJECT
298
299 /** @brief Currently selected color in the dialog
300 *
301 * @invariant This property is provided as an RGB value.
302 * <tt>QColor::isValid()</tt> is always <tt>true</tt> and
303 * <tt>QColor::spec()</tt> is always <tt>QColor::Spec::Rgb</tt>.
304 *
305 * @invariant The signal @ref currentColorChanged() is emitted always and
306 * only when the value of this property changes.
307 *
308 * @note The setter @ref setCurrentColor() does not accept all QColor
309 * values. See its documentation for details.
310 *
311 * @sa READ @ref currentColor() const
312 * @sa WRITE @ref setCurrentColor()
313 * @sa NOTIFY @ref currentColorChanged()
314 *
315 * @internal
316 *
317 * @note This property does not have <tt>USER true</tt>. While it would
318 * be nice to have it, we do not do this because of conformance with
319 * QColorDialog, which doesn’t have it either. */
320 Q_PROPERTY(QColor currentColor READ currentColor WRITE setCurrentColor NOTIFY currentColorChanged)
321
322 /** @brief Layout dimensions
323 *
324 * Defines if the dialog uses a rather collapsed (small) or a rather
325 * expanded (large) layout. In both cases, all elements are present.
326 * But for the collapsed variant, more elements are put in
327 * tab widgets, while for the expanded variant, more elements are
328 * visible at the same time.
329 *
330 * @image html ColorDialogCollapsed.png "collapsed" height=300
331 * @image html ColorDialogExpanded.png "expanded" height=300
332 *
333 * Default value:
334 * @snippet src/colordialog_p.h layoutDimensionsDefaultValue
335 *
336 * When the layout dimension effectively changes, also the dialog size
337 * is adapted.
338 *
339 * @sa @ref DialogLayoutDimensions
340 * @sa READ @ref layoutDimensions() const
341 * @sa WRITE @ref setLayoutDimensions()
342 * @sa NOTIFY @ref layoutDimensionsChanged
343 *
344 * @internal
345 *
346 * @sa @ref ColorDialogPrivate::m_layoutDimensionsEffective
347 *
348 * @todo Remove this property? Instead, implement a truly convertible
349 * layout: Show/hide the widget for numbers depending on dialog size,
350 * and maybe even re-arrange even more of the widgets with varying
351 * sizes. */
352 Q_PROPERTY(DialogLayoutDimensions layoutDimensions READ layoutDimensions WRITE setLayoutDimensions NOTIFY layoutDimensionsChanged)
353
354 /** @brief Various options that affect the look and feel of the dialog
355 *
356 * These are the same settings as for QColorDialog. For compatibility
357 * reasons, they are also of the same type: @ref ColorDialogOptions
358 *
359 * | Option | Default value | Description
360 * | :------------------ | :------------ | :----------
361 * | ShowAlphaChannel | false | Allow the user to select the alpha component of a color.
362 * | NoButtons | false | Don't display OK and Cancel buttons. (Useful for “live dialogs”.)
363 * | DontUseNativeDialog | true | Use Qt’s standard color dialog instead of the operating system native color dialog.
364 *
365 * @invariant The option <tt>ColorDialogOption::DontUseNativeDialog</tt>
366 * will always be <tt>true</tt> because it’s just the point of
367 * this library to provide an own, non-native dialog. (If you
368 * set <tt>ColorDialogOption::DontUseNativeDialog</tt> explicitly
369 * to <tt>false</tt>, this will silently be ignored, while the
370 * other options that you might have set, will be correctly applied.)
371 *
372 * Example:
373 * @snippet testcolordialog.cpp setOptionsWithLocalEnum
374 * Or:
375 * @snippet testcolordialog.cpp setOptionsWithQColorDialogEnum
376 * @note At difference to QColorDialog, you need a fully qualified
377 * identifier for the enum values. The following code would therefore
378 * fail:<br/>
379 * <tt>myDialog->setOption(ShowAlphaChannel, false);</tt>
380 *
381 * @sa READ @ref options() const
382 * @sa @ref testOption()
383 * @sa WRITE @ref setOptions()
384 * @sa @ref setOption()
385 * @sa NOTIFY @ref optionsChanged()*/
386 Q_PROPERTY(ColorDialogOptions options READ options WRITE setOptions NOTIFY optionsChanged)
387
388public:
389 /** @brief Local alias for QColorDialog::ColorDialogOption
390 *
391 * This type is declared as type to Qt’s type system via
392 * <tt>Q_DECLARE_METATYPE</tt>. Depending on your use case (for
393 * example if you want to use for <em>queued</em> signal-slot connections),
394 * you might consider calling <tt>qRegisterMetaType()</tt> for
395 * this type, once you have a QApplication object. */
397 /** @brief Local alias for QColorDialog::ColorDialogOptions
398 *
399 * This type is declared as type to Qt’s type system via
400 * <tt>Q_DECLARE_METATYPE</tt>. Depending on your use case (for
401 * example if you want to use for <em>queued</em> signal-slot connections),
402 * you might consider calling <tt>qRegisterMetaType()</tt> for
403 * this type, once you have a QApplication object. */
405 /** @brief Layout dimensions
406 *
407 * This enum is declared to the meta-object system with <tt>Q_ENUM</tt>.
408 * This happens automatically. You do not need to make any manual calls.
409 *
410 * This type is declared as type to Qt’s type system via
411 * <tt>Q_DECLARE_METATYPE</tt>. Depending on your use case (for
412 * example if you want to use for <em>queued</em> signal-slot connections),
413 * you might consider calling <tt>qRegisterMetaType()</tt> for
414 * this type, once you have a QApplication object. */
416 ScreenSizeDependent, /**< Decide automatically between
417 <tt>collapsed</tt> and <tt>expanded</tt> layout: <tt>collapsed</tt>
418 is used on small screens, and <tt>expanded</tt> on big screens. The
419 decision is based on the screen size of the <em>default screen</em>
420 of the widget (see <tt>QGuiApplication::primaryScreen()</tt> for
421 details). The decision is evaluated at the moment when setting this
422 value, and again each time the widget is shown again. It is
423 <em>not</em> evaluated again when a yet existing dialog is just
424 moved to another screen. */
425 Collapsed, /**< Use the small, “collapsed“ layout of this dialog. */
426 Expanded /**< Use the large, “expanded” layout of this dialog. */
427 };
428 Q_ENUM(DialogLayoutDimensions)
429 Q_INVOKABLE explicit ColorDialog(QWidget *parent = nullptr);
430 Q_INVOKABLE explicit ColorDialog(const QColor &initial, QWidget *parent = nullptr);
431 Q_INVOKABLE explicit ColorDialog(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, QWidget *parent = nullptr);
432 Q_INVOKABLE explicit ColorDialog(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, const QColor &initial, QWidget *parent = nullptr);
433 virtual ~ColorDialog() noexcept override;
434 /** @brief Getter for property @ref currentColor
435 * @returns the property @ref currentColor */
436 [[nodiscard]] QColor currentColor() const;
437 [[nodiscard]] static QColor
438 getColor(const QColor &initial = Qt::white, QWidget *parent = nullptr, const QString &title = QString(), ColorDialogOptions options = ColorDialogOptions());
439 [[nodiscard]] static QColor getColor(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace,
440 const QColor &initial = Qt::white,
441 QWidget *parent = nullptr,
442 const QString &title = QString(),
443 ColorDialogOptions options = ColorDialogOptions());
444 /** @brief Getter for property @ref layoutDimensions
445 * @returns the property @ref layoutDimensions */
446 [[nodiscard]] ColorDialog::DialogLayoutDimensions layoutDimensions() const;
447 // Make sure not to override the base class’s “open“ function:
448 using QDialog::open;
449 Q_INVOKABLE void open(QObject *receiver, const char *member);
450 /** @brief Getter for property @ref options
451 * @returns the current @ref options */
452 [[nodiscard]] ColorDialogOptions options() const;
453 [[nodiscard]] Q_INVOKABLE QColor selectedColor() const;
454 virtual void setVisible(bool visible) override;
455 [[nodiscard]] Q_INVOKABLE bool testOption(PerceptualColor::ColorDialog::ColorDialogOption option) const;
456
457public Q_SLOTS:
458 void setCurrentColor(const QColor &color);
459 void setLayoutDimensions(const PerceptualColor::ColorDialog::DialogLayoutDimensions newLayoutDimensions);
460 Q_INVOKABLE void setOption(PerceptualColor::ColorDialog::ColorDialogOption option, bool on = true);
461 void setOptions(PerceptualColor::ColorDialog::ColorDialogOptions newOptions);
462
463Q_SIGNALS:
464 /** @brief This signal is emitted just after the user has clicked OK to
465 * select a color to use.
466 * @param color the chosen color */
467 void colorSelected(const QColor &color);
468 /** @brief Notify signal for property @ref currentColor.
469 *
470 * This signal is emitted whenever the “current color” changes in the
471 * dialog.
472 * @param color the new “current color” */
473 void currentColorChanged(const QColor &color);
474 /** @brief Notify signal for property @ref layoutDimensions.
475 * @param newLayoutDimensions the new layout dimensions */
476 void layoutDimensionsChanged(const PerceptualColor::ColorDialog::DialogLayoutDimensions newLayoutDimensions);
477 /** @brief Notify signal for property @ref options.
478 * @param newOptions the new options */
479 void optionsChanged(const PerceptualColor::ColorDialog::ColorDialogOptions newOptions);
480
481protected:
482 virtual void changeEvent(QEvent *event) override;
483 virtual void done(int result) override;
484 virtual void showEvent(QShowEvent *event) override;
485
486private:
487 Q_DISABLE_COPY(ColorDialog)
488
489 /** @internal
490 *
491 * @brief Declare the private implementation as friend class.
492 *
493 * This allows the private class to access the protected members and
494 * functions of instances of <em>this</em> class. */
495 friend class ColorDialogPrivate;
496 /** @brief Pointer to implementation (pimpl) */
497 ConstPropagatingUniquePointer<ColorDialogPrivate> d_pointer;
498
499 /** @internal @brief Only for unit tests. */
500 friend class TestColorDialog;
501};
502
503} // namespace PerceptualColor
504
508
509#endif // COLORDIALOG_H
A perceptually uniform color picker dialog.
DialogLayoutDimensions
Layout dimensions.
QColorDialog::ColorDialogOptions ColorDialogOptions
Local alias for QColorDialog::ColorDialogOptions.
Q_INVOKABLE QColor getColor(QString name)
This file provides support for C++ symbol import and export.
#define PERCEPTUALCOLOR_IMPORTEXPORT
A macro that either exports dynamic library symbols or imports dynamic library symbols or does nothin...
The namespace of this library.
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.