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