File indexing completed on 2024-05-12 04:44:32

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef COLORWHEEL_H
0005 #define COLORWHEEL_H
0006 
0007 #include "abstractdiagram.h"
0008 #include "constpropagatinguniquepointer.h"
0009 #include "importexport.h"
0010 #include <qglobal.h>
0011 #include <qsharedpointer.h>
0012 #include <qsize.h>
0013 class QKeyEvent;
0014 class QMouseEvent;
0015 class QPaintEvent;
0016 class QResizeEvent;
0017 class QWheelEvent;
0018 class QWidget;
0019 
0020 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0021 #include <qtmetamacros.h>
0022 #else
0023 #include <qobjectdefs.h>
0024 #include <qstring.h>
0025 class QObject;
0026 #endif
0027 
0028 namespace PerceptualColor
0029 {
0030 class ColorWheelPrivate;
0031 
0032 class RgbColorSpace;
0033 
0034 /** @brief A color wheel widget.
0035  *
0036  * This widget allows the user to choose the hue (as defined in the LCH
0037  * color space).
0038  *
0039  * @image html ColorWheel.png "ColorWheel" width=200
0040  *
0041  * @note This widget <em>always</em> accepts focus by a mouse click within
0042  * the circle. This happens regardless of the <tt>QWidget::focusPolicy</tt>
0043  * property:
0044  * - If you set the <tt>QWidget::focusPolicy</tt> property to a
0045  *   value that does not accept focus by mouse click, the focus
0046  *   will nevertheless be accepted for clicks within the actual circle.
0047  *   (This is the default behavior.)
0048  * - If you set the <tt>QWidget::focusPolicy</tt> property to a
0049  *   value that accepts focus by mouse click, the focus will not only be
0050  *   accepted for clicks within the actual circle, but also for clicks
0051  *   anywhere within the (rectangular) widget.
0052  *
0053  * @internal
0054  *
0055  * @todo Add support for Qt::MouseButton::BackButton?
0056  * (Typically present on the 'thumb' side of a mouse
0057  * with extra buttons. This is NOT the tilt wheel.)
0058  * Add support for Qt::MouseButton::ForwardButton?
0059  * (Typically present beside the 'Back' button, and
0060  * also pressed by the thumb.)
0061  *
0062  * @todo What when some of the wheel colors are out of gamut? How to handle
0063  * that? */
0064 class PERCEPTUALCOLOR_IMPORTEXPORT ColorWheel : public AbstractDiagram
0065 {
0066     Q_OBJECT
0067 
0068     /** @brief The currently selected hue.
0069      *
0070      * This is the hue angle, as defined in the LCH color model.
0071      *
0072      *
0073      * Measured in degree.
0074      *
0075      * Valid range: [0°, 360°[. The widget accepts initially also
0076      * out-of-range values, but once a user interaction has taken
0077      * place, it will hold a normalized value. So
0078      * \li 0 gets 0
0079      * \li 359.9 gets 359.9
0080      * \li 360 gets 0
0081      * \li 361.2 gets 1.2
0082      * \li 720 gets 0
0083      * \li -1 gets 359
0084      * \li -1.3 gets 358.7
0085      *
0086      * @sa READ @ref hue() const
0087      * @sa WRITE @ref setHue()
0088      * @sa NOTIFY @ref hueChanged()
0089      *
0090      * @internal
0091      *
0092      * The value gets normalized according
0093      * to @ref PolarPointF::normalizedAngle360() */
0094     Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged USER true)
0095 
0096 public:
0097     Q_INVOKABLE explicit ColorWheel(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, QWidget *parent = nullptr);
0098     virtual ~ColorWheel() noexcept override;
0099     /** @brief Getter for property @ref hue
0100      *  @returns the property @ref hue */
0101     [[nodiscard]] qreal hue() const;
0102     [[nodiscard]] virtual QSize minimumSizeHint() const override;
0103     [[nodiscard]] virtual QSize sizeHint() const override;
0104 
0105 Q_SIGNALS:
0106     /** @brief Notify signal for property @ref hue.
0107      * @param newHue the new hue */
0108     void hueChanged(const qreal newHue);
0109 
0110 public Q_SLOTS:
0111     void setHue(const qreal newHue);
0112 
0113 protected:
0114     virtual void keyPressEvent(QKeyEvent *event) override;
0115     virtual void mouseMoveEvent(QMouseEvent *event) override;
0116     virtual void mousePressEvent(QMouseEvent *event) override;
0117     virtual void mouseReleaseEvent(QMouseEvent *event) override;
0118     virtual void paintEvent(QPaintEvent *event) override;
0119     virtual void resizeEvent(QResizeEvent *event) override;
0120     virtual void wheelEvent(QWheelEvent *event) override;
0121 
0122 private:
0123     Q_DISABLE_COPY(ColorWheel)
0124 
0125     /** @internal
0126      *
0127      * @brief Declare the private implementation as friend class.
0128      *
0129      * This allows the private class to access the protected members and
0130      * functions of instances of <em>this</em> class. */
0131     friend class ColorWheelPrivate;
0132     /** @brief Pointer to implementation (pimpl) */
0133     ConstPropagatingUniquePointer<ColorWheelPrivate> d_pointer;
0134 
0135     /** @internal @brief Only for unit tests. */
0136     friend class TestColorWheel;
0137 
0138     /** @internal
0139      * @brief Internal friend declaration.
0140      *
0141      * This class is used as child class in @ref WheelColorPicker.
0142      * There is a tight collaboration. */
0143     friend class WheelColorPicker;
0144     /** @internal
0145      * @brief Internal friend declaration.
0146      *
0147      * This class is used as child class in @ref WheelColorPicker.
0148      * There is a tight collaboration. */
0149     friend class WheelColorPickerPrivate;
0150 };
0151 
0152 } // namespace PerceptualColor
0153 
0154 #endif // COLORWHEEL_H