File indexing completed on 2024-05-12 04:44:33
0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com> 0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT 0003 0004 #ifndef GRADIENTSLIDER_H 0005 #define GRADIENTSLIDER_H 0006 0007 #include "abstractdiagram.h" 0008 #include "constpropagatinguniquepointer.h" 0009 #include "importexport.h" 0010 #include "lchadouble.h" 0011 #include <qglobal.h> 0012 #include <qmetatype.h> 0013 #include <qnamespace.h> 0014 #include <qsharedpointer.h> 0015 #include <qsize.h> 0016 class QKeyEvent; 0017 class QMouseEvent; 0018 class QPaintEvent; 0019 class QResizeEvent; 0020 class QWheelEvent; 0021 class QWidget; 0022 0023 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0024 #include <qtmetamacros.h> 0025 #else 0026 #include <qobjectdefs.h> 0027 #include <qstring.h> 0028 class QObject; 0029 #endif 0030 0031 namespace PerceptualColor 0032 { 0033 class GradientSliderPrivate; 0034 0035 class RgbColorSpace; 0036 0037 /** @brief A slider who’s groove displays an LCH color gradient. 0038 * 0039 * @image html GradientSlider.png "GradientSlider" width=200 0040 * 0041 * The groove of this slider that displays a gradient between two LCH 0042 * colors. The gradient is an equal gradient calculated independently 0043 * for each of the four components (lightness, chroma, hue, alpha). 0044 * 0045 * The hue component is the only one that is circular (0° = 360°): Here, 0046 * Here, the path via the shorter side is always chosen. Examples: 0047 * @li If the first hue is 182° and the second hue is 1°, than 0048 * the hue will increase from 182° up to 359°, than 0° and then 1°. 0049 * @li If the first hue is 169° and the second hue is 359°, than 0050 * the hue will decrease from 169° down to 0°, and then 359°. 0051 * 0052 * This widget considers the alpha channel, using a background 0053 * of gray squares behind the (semi-)transparent colors. 0054 * 0055 * Example: 0056 * | | L | C | h | alpha | 0057 * | :--------------- | --: | -: | ---: | ----: | 0058 * | @ref firstColor | 80% | 5 | 15° | 0.7 | 0059 * | | 70% | 7 | 5° | 0.8 | 0060 * | | 60% | 9 | 355° | 0.9 | 0061 * | @ref secondColor | 50% | 11 | 345° | 1.0 | 0062 * 0063 * Note that due to this mathematical model, there might be out-of-gamut colors 0064 * within the slider even if both, the first and the second color are in-gamut 0065 * colors. Out-of-gamut colors are rendered as nearby in-gamut colors. 0066 * 0067 * - In the case of vertical @ref orientation, @ref firstColor is the colour 0068 * at the bottom of the widget and @ref secondColor is the colour at the 0069 * top of the widget. 0070 * - In the case of horizontal @ref orientation, @ref firstColor is the 0071 * colour on the left of the widget and @ref secondColor is the colour 0072 * on the right of the widget in LTR layout. In RTL layout it is the 0073 * other way round. 0074 * 0075 * @internal 0076 * 0077 * @todo A better handle for the slider. Currently, the handle is just a 0078 * line. At a handle position at the very beginning or end of the slider, 0079 * furthermore only half of the line thickness is visible. It might be better 0080 * to have arrows outside the slider to mark the position. (On the other 0081 * hand, this would be different to the slider handles of the color 0082 * wheels…) 0083 * 0084 * @todo A better focus indicator. Some example code is commented out 0085 * in the implementation of @ref paintEvent(). 0086 * 0087 * @todo This class @ref GradientSlider and also the class 0088 * @ref ColorWheel could be subclasses of QAbstractSlider. This 0089 * might integrate better with the user’s Qt code. On the other hand, 0090 * this would mean a lot of work in this library to implement the 0091 * complete interface of QAbstractSlider, and probably we would 0092 * also need multiple inheritance because this class also depends 0093 * on @ref AbstractDiagram which is itself yet a subclass of QWidget. 0094 */ 0095 // The API is roughly orientated on QSlider/QAbstractSlider and on 0096 // KSelecter/KGradientSelector where applicable. Our API is however 0097 // less complete, and of course also a little bit different, as 0098 // both, QAbstractSlider and KGradientSelector are not directly 0099 // comparable to this class. 0100 class PERCEPTUALCOLOR_IMPORTEXPORT GradientSlider : public AbstractDiagram 0101 { 0102 Q_OBJECT 0103 0104 /** @brief First color (the one corresponding to a low @ref value) 0105 * 0106 * @sa READ @ref firstColor() const 0107 * @sa WRITE @ref setFirstColor() 0108 * @sa NOTIFY @ref firstColorChanged() 0109 * @sa @ref secondColor */ 0110 Q_PROPERTY(PerceptualColor::LchaDouble firstColor READ firstColor WRITE setFirstColor NOTIFY firstColorChanged) 0111 0112 /** @brief Orientation of the widget. 0113 * 0114 * By default, the orientation is horizontal. The possible 0115 * orientations are <tt>Qt::Horizontal</tt> and <tt>Qt::Vertical</tt>. 0116 * 0117 * Also, <tt>Qt::Orientation</tt> is declared in this header as type to 0118 * Qt’s type system: <tt>Q_DECLARE_METATYPE(Qt::Orientation)</tt>. This 0119 * is done because Qt itself does not declare this type as meta type. 0120 * Because we use it here in a property including a signal, we have to 0121 * declare this type. Depending on your use case (for example if you 0122 * want to use it reliably in Qt’s signals and slots), you might consider 0123 * calling <tt>qRegisterMetaType()</tt> for this type, once you have 0124 * a QApplication object. 0125 * 0126 * @sa READ @ref orientation() const 0127 * @sa WRITE @ref setOrientation() 0128 * @sa NOTIFY @ref orientationChanged() */ 0129 Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) 0130 0131 /** @brief This property holds the page step. 0132 * 0133 * The larger of two natural steps this widget provides. 0134 * Corresponds to the user pressing PageUp or PageDown. 0135 * 0136 * The valid range is <tt>[0, 1]</tt>. 0137 * 0138 * @sa READ @ref pageStep() const 0139 * @sa WRITE @ref setPageStep() 0140 * @sa NOTIFY @ref pageStepChanged() 0141 * @sa @ref singleStep */ 0142 Q_PROPERTY(qreal pageStep READ pageStep WRITE setPageStep NOTIFY pageStepChanged) 0143 0144 /** @brief Second color (the one corresponding to a high @ref value) 0145 * 0146 * @sa READ @ref secondColor() const 0147 * @sa WRITE @ref setSecondColor() 0148 * @sa NOTIFY @ref secondColorChanged() 0149 * @sa @ref firstColor */ 0150 Q_PROPERTY(PerceptualColor::LchaDouble secondColor READ secondColor WRITE setSecondColor NOTIFY secondColorChanged) 0151 0152 /** @brief This property holds the single step. 0153 * 0154 * The smaller of two natural steps this widget provides. 0155 * Corresponds to the user pressing an arrow key. 0156 * 0157 * The valid range is <tt>[0, 1]</tt>. 0158 * 0159 * @sa READ @ref singleStep() const 0160 * @sa WRITE @ref setSingleStep() 0161 * @sa NOTIFY @ref singleStepChanged() 0162 * @sa @ref pageStep */ 0163 Q_PROPERTY(qreal singleStep READ singleStep WRITE setSingleStep NOTIFY singleStepChanged) 0164 0165 /** @brief The slider’s current value. 0166 * 0167 * 0168 * The valid range is <tt>[0, 1]</tt>. 0169 * The slider forces the value to be within the valid range: 0170 * <tt>0 <= value <= 1</tt>. 0171 * - <tt>0</tt> means: totally firstColor() 0172 * - <tt>1</tt> means: totally secondColor() 0173 * 0174 * @sa READ @ref value() const 0175 * @sa WRITE @ref setValue() 0176 * @sa NOTIFY @ref valueChanged() */ 0177 Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged USER true) 0178 0179 public: 0180 Q_INVOKABLE explicit GradientSlider(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, QWidget *parent = nullptr); 0181 Q_INVOKABLE explicit GradientSlider(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, 0182 Qt::Orientation orientation, 0183 QWidget *parent = nullptr); 0184 virtual ~GradientSlider() noexcept override; 0185 /** @brief Getter for property @ref firstColor 0186 * @returns the property */ 0187 [[nodiscard]] PerceptualColor::LchaDouble firstColor() const; 0188 [[nodiscard]] virtual QSize minimumSizeHint() const override; 0189 /** @brief Getter for property @ref orientation 0190 * @returns the property */ 0191 [[nodiscard]] Qt::Orientation orientation() const; 0192 /** @brief Getter for property @ref pageStep 0193 * @returns the property */ 0194 [[nodiscard]] qreal pageStep() const; 0195 /** @brief Getter for property @ref secondColor 0196 * @returns the property */ 0197 [[nodiscard]] PerceptualColor::LchaDouble secondColor() const; 0198 /** @brief Getter for property @ref singleStep 0199 * @returns the property */ 0200 [[nodiscard]] qreal singleStep() const; 0201 [[nodiscard]] virtual QSize sizeHint() const override; 0202 /** @brief Getter for property @ref value 0203 * @returns the property */ 0204 [[nodiscard]] qreal value() const; 0205 0206 Q_SIGNALS: 0207 /** @brief Signal for @ref firstColor property. 0208 * @param newFirstColor the new @ref firstColor */ 0209 void firstColorChanged(const PerceptualColor::LchaDouble &newFirstColor); 0210 /** @brief Signal for @ref orientation property. 0211 * @param newOrientation the new @ref orientation */ 0212 void orientationChanged(const Qt::Orientation newOrientation); 0213 /** @brief Signal for @ref pageStep property. 0214 * @param newPageStep the new @ref pageStep */ 0215 void pageStepChanged(const qreal newPageStep); 0216 /** @brief Signal for @ref secondColor property. 0217 * @param newSecondColor the new @ref secondColor */ 0218 void secondColorChanged(const PerceptualColor::LchaDouble &newSecondColor); 0219 /** @brief Signal for @ref singleStep property. 0220 * @param newSingleStep the new @ref singleStep */ 0221 void singleStepChanged(const qreal newSingleStep); 0222 /** @brief Signal for @ref value property. 0223 * @param newValue the new @ref value */ 0224 void valueChanged(const qreal newValue); 0225 0226 public Q_SLOTS: 0227 void setColors(const PerceptualColor::LchaDouble &newFirstColor, const PerceptualColor::LchaDouble &newSecondColor); 0228 void setFirstColor(const PerceptualColor::LchaDouble &newFirstColor); 0229 void setOrientation(const Qt::Orientation newOrientation); 0230 void setPageStep(const qreal newPageStep); 0231 void setSecondColor(const PerceptualColor::LchaDouble &newSecondColor); 0232 void setSingleStep(const qreal newSingleStep); 0233 void setValue(const qreal newValue); 0234 0235 protected: 0236 virtual void keyPressEvent(QKeyEvent *event) override; 0237 virtual void mouseMoveEvent(QMouseEvent *event) override; 0238 virtual void mousePressEvent(QMouseEvent *event) override; 0239 virtual void mouseReleaseEvent(QMouseEvent *event) override; 0240 virtual void paintEvent(QPaintEvent *event) override; 0241 virtual void resizeEvent(QResizeEvent *event) override; 0242 virtual void wheelEvent(QWheelEvent *event) override; 0243 0244 private: 0245 Q_DISABLE_COPY(GradientSlider) 0246 0247 /** @internal 0248 * 0249 * @brief Declare the private implementation as friend class. 0250 * 0251 * This allows the private class to access the protected members and 0252 * functions of instances of <em>this</em> class. */ 0253 friend class GradientSliderPrivate; 0254 /** @brief Pointer to implementation (pimpl) */ 0255 ConstPropagatingUniquePointer<GradientSliderPrivate> d_pointer; 0256 0257 /** @internal @brief Only for unit tests. */ 0258 friend class TestGradientSlider; 0259 }; 0260 0261 } // namespace PerceptualColor 0262 0263 Q_DECLARE_METATYPE(Qt::Orientation) 0264 0265 #endif // GRADIENTSLIDER_H