File indexing completed on 2024-05-12 04:44:29
0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com> 0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT 0003 0004 #ifndef CHROMAHUEDIAGRAM_H 0005 #define CHROMAHUEDIAGRAM_H 0006 0007 #include "abstractdiagram.h" 0008 #include "constpropagatinguniquepointer.h" 0009 #include "importexport.h" 0010 #include "lchdouble.h" 0011 #include <qglobal.h> 0012 #include <qsharedpointer.h> 0013 #include <qsize.h> 0014 0015 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0016 #include <qtmetamacros.h> 0017 #else 0018 #include <qobjectdefs.h> 0019 #include <qstring.h> 0020 class QObject; 0021 #endif 0022 0023 class QKeyEvent; 0024 class QMouseEvent; 0025 class QPaintEvent; 0026 class QResizeEvent; 0027 class QWheelEvent; 0028 class QWidget; 0029 0030 namespace PerceptualColor 0031 { 0032 class ChromaHueDiagramPrivate; 0033 } 0034 namespace PerceptualColor 0035 { 0036 class RgbColorSpace; 0037 } 0038 0039 namespace PerceptualColor 0040 { 0041 /** @brief A widget for selecting chroma and hue in LCH color space 0042 * 0043 * This widget displays the plan of chroma and hue 0044 * (that means a diagram of the radius and the angle of the 0045 * LCH color space respectively the a axis and the b axis of the 0046 * <a href="https://en.wikipedia.org/wiki/CIELAB_color_space"> 0047 * Lab color model</a>) at a given lightness. 0048 * 0049 * @image html ChromaHueDiagram.png "ChromaHueDiagram" width=250 0050 * 0051 * The widget allows the user to select a color (chroma and hue) within the 0052 * specified gamut at a given lightness. It reacts on mouse events and on 0053 * keyboard events (see @ref keyPressEvent() for details). 0054 * 0055 * The form of the selection handle (that always indicates the distance from 0056 * the center of the diagram) and the circular form of the widget, all this 0057 * helps the user to understand intuitively that he is moving within a 0058 * polar coordinate system and to capture easily the current radius 0059 * and angle. 0060 * 0061 * Usage example: @snippet testchromahuediagram.cpp instantiate 0062 * 0063 * @note This widget <em>always</em> accepts focus by a mouse click within 0064 * the circle. This happens regardless of the <tt>QWidget::focusPolicy</tt> 0065 * property: 0066 * - If you set the <tt>QWidget::focusPolicy</tt> property to a 0067 * value that does not accept focus by mouse click, the focus 0068 * will nevertheless be accepted for clicks within the actual circle. 0069 * (This is the default behavior.) 0070 * - If you set the <tt>QWidget::focusPolicy</tt> property to a 0071 * value that accepts focus by mouse click, the focus will not only be 0072 * accepted for clicks within the actual circle, but also for clicks 0073 * anywhere within the (rectangular) widget. 0074 * 0075 * @internal 0076 * 0077 * @todo BUG Left-click in the gray area inside the wheel but outside 0078 * the displayed gamut; maintain the click button and do not move the 0079 * mouse. Actual behavior: Mouse cursor is invisible. Expected behaviour: 0080 * Mouse cursor stays visible (as it would be anyway after moving the mouse). 0081 * 0082 * @todo BUG Click on the wheel. Actual behaviour: Nothing. Expected behavior: 0083 * The selected color follows the cursor. 0084 * 0085 * @todo BUG Wide gamut RGB: RGB 51 255 51. Chroma-Hue-Diagram: The handle 0086 * is drawn outside the circle. This should never happen! See also 0087 * @ref PerceptualColor::CielchD50Values::maximumChroma 0088 * 0089 * @todo The hue circle around chroma-hue diagram might be confusing because 0090 * it is colored, but it is not a usable slider like all other colored 0091 * elements. We could remove it. But on the other hand, it is also useful 0092 * to have it. Maybe make it look different than for @ref WheelColorPicker, 0093 * for instance make it thinner and make it touch the gray diagram area? 0094 * Maybe make it react on mouse events just like the inner part of the diagram. 0095 * 0096 * @todo Add a circular indicator to the handle, indicating the values 0097 * with identical chroma? Only during mouse dragging? Or always? Or never? 0098 * 0099 * @todo Example code: How to create the widget at a given 0100 * lightness. 0101 * 0102 * @todo Allow to touch the widget on the color wheel (and have a reaction). 0103 * 0104 * @todo Use a cross cursor for better usability: The cross cursor indicates 0105 * to the user that an area can be clicked in. Do it only within the gamut 0106 * (where the color handle can actually go) or in the hole gray circle, 0107 * which is the mouse sensitive area (but out of the gamut the color 0108 * handle cannot follow)? 0109 * 0110 * @todo Support additional mouse buttons. For example, “forward” and 0111 * “backward” could be used to increase or decrease the radius. 0112 * 0113 * @todo What if black or white are out of gamut on L=0.1 or L=99.9? Where 0114 * are the handles placed? Visible or invisible? How to react? Should 0115 * there be always a physical pixel in the middle that is visible (black 0116 * or white) even when out of gamut? 0117 * 0118 * @todo Optimization: It might be possible to <em>not</em> store 0119 * both, @ref ChromaHueDiagramPrivate::m_chromaHueImage and 0120 * a @ref ChromaHueDiagramPrivate::m_wheelImage. Instead, both could 0121 * be combined into one single image. As long as there is a (big enough) 0122 * safety margin between the color wheel and the inner (circular) diagram 0123 * surface, it should be possible to erase the original data and paint 0124 * new data above without rendering artefacts for each of these two elements 0125 * of the image. */ 0126 class PERCEPTUALCOLOR_IMPORTEXPORT ChromaHueDiagram : public AbstractDiagram 0127 { 0128 Q_OBJECT 0129 0130 /** @brief Currently selected color 0131 * 0132 * The widget allows the user to change the LCH chroma and the LCH hue 0133 * values. However, the LCH lightness value cannot be changed by the 0134 * user, but only by the programmer through this property. 0135 * 0136 * The programmer can set this property to out-of-gamut values; the 0137 * user cannot. 0138 * 0139 * @sa READ @ref currentColor() const 0140 * @sa WRITE @ref setCurrentColor() 0141 * @sa NOTIFY @ref currentColorChanged() */ 0142 Q_PROPERTY(LchDouble currentColor READ currentColor WRITE setCurrentColor NOTIFY currentColorChanged) 0143 0144 public: 0145 Q_INVOKABLE explicit ChromaHueDiagram(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, QWidget *parent = nullptr); 0146 virtual ~ChromaHueDiagram() noexcept override; 0147 /** @brief Getter for property @ref currentColor 0148 * @returns the property @ref currentColor */ 0149 [[nodiscard]] LchDouble currentColor() const; 0150 [[nodiscard]] virtual QSize minimumSizeHint() const override; 0151 [[nodiscard]] virtual QSize sizeHint() const override; 0152 0153 public Q_SLOTS: 0154 void setCurrentColor(const PerceptualColor::LchDouble &newCurrentColor); 0155 0156 Q_SIGNALS: 0157 /** @brief Notify signal for property @ref currentColor. 0158 * @param newCurrentColor the new current color */ 0159 void currentColorChanged(const PerceptualColor::LchDouble &newCurrentColor); 0160 0161 protected: 0162 virtual void keyPressEvent(QKeyEvent *event) override; 0163 virtual void mouseMoveEvent(QMouseEvent *event) override; 0164 virtual void mousePressEvent(QMouseEvent *event) override; 0165 virtual void mouseReleaseEvent(QMouseEvent *event) override; 0166 virtual void paintEvent(QPaintEvent *event) override; 0167 virtual void resizeEvent(QResizeEvent *event) override; 0168 virtual void wheelEvent(QWheelEvent *event) override; 0169 0170 private: 0171 Q_DISABLE_COPY(ChromaHueDiagram) 0172 0173 /** @internal 0174 * 0175 * @brief Declare the private implementation as friend class. 0176 * 0177 * This allows the private class to access the protected members and 0178 * functions of instances of <em>this</em> class. */ 0179 friend class ChromaHueDiagramPrivate; 0180 /** @brief Pointer to implementation (pimpl) */ 0181 ConstPropagatingUniquePointer<ChromaHueDiagramPrivate> d_pointer; 0182 0183 /** @internal @brief Only for unit tests. */ 0184 friend class TestChromaHueDiagram; 0185 }; 0186 0187 } // namespace PerceptualColor 0188 0189 #endif // CHROMAHUEDIAGRAM_H