File indexing completed on 2024-05-12 16:02:03
0001 /* 0002 * KDE. Krita Project. 0003 * 0004 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #ifndef KIS_LEVELS_SLIDER_H 0010 #define KIS_LEVELS_SLIDER_H 0011 0012 #include <QWidget> 0013 #include <QVector> 0014 0015 #include "kritawidgets_export.h" 0016 0017 /** 0018 * @brief A base class for levels slider like widgets: a slider with a gradient 0019 * and multiple handles 0020 */ 0021 class KRITAWIDGETS_EXPORT KisLevelsSlider : public QWidget 0022 { 0023 Q_OBJECT 0024 0025 public: 0026 KisLevelsSlider(QWidget *parent); 0027 ~KisLevelsSlider(); 0028 0029 QSize sizeHint() const override; 0030 QSize minimumSizeHint() const override; 0031 0032 /** 0033 * @brief Gets the normalized position of a given handle 0034 */ 0035 qreal handlePosition(int handleIndex) const; 0036 /** 0037 * @brief Gets the color associated with a given handle 0038 */ 0039 QColor handleColor(int handleIndex) const; 0040 /** 0041 * @brief Gets the rect where the gradient will be painted 0042 */ 0043 virtual QRect gradientRect() const; 0044 0045 public Q_SLOTS: 0046 /** 0047 * @brief Sets the normalized position of the given handle 0048 */ 0049 virtual void setHandlePosition(int handleIndex, qreal newPosition); 0050 /** 0051 * @brief Sets the color associated with the given handle 0052 */ 0053 virtual void setHandleColor(int handleIndex, const QColor &newColor); 0054 0055 Q_SIGNALS: 0056 /** 0057 * @brief Signal emited when the position of a handle changes 0058 */ 0059 void handlePositionChanged(int handleIndex, qreal position); 0060 /** 0061 * @brief Signal emited when the color associated with a handle changes 0062 */ 0063 void handleColorChanged(int handleIndex, const QColor &color); 0064 0065 protected: 0066 struct Handle 0067 { 0068 int index; 0069 qreal position; 0070 QColor color; 0071 }; 0072 0073 static constexpr int handleWidth{11}; 0074 static constexpr int handleHeight{11}; 0075 static constexpr qreal minimumSpaceBetweenHandles{0.001}; 0076 static constexpr qreal normalPositionIncrement{0.01}; 0077 static constexpr qreal slowPositionIncrement{0.001}; 0078 0079 /** 0080 * @brief The collection of handles 0081 */ 0082 QVector<Handle> m_handles; 0083 /** 0084 * @brief This variable indicates if the handles can have unordered 0085 * positions. If it is set to true then the user won't be able to move a 0086 * handle pass another one. If it is set to false then the ser will be able 0087 * to move the handles freely 0088 */ 0089 int m_constrainPositions; 0090 0091 int m_selectedHandle; 0092 int m_hoveredHandle; 0093 0094 /** 0095 * @brief Regardless the index of a handle, they can be unordered in terms 0096 * of the position. This returns a sorted vector with the handles that have 0097 * a smaller position first. If two handles have the same position then the 0098 * index is used for sorting 0099 */ 0100 QVector<Handle> sortedHandles() const; 0101 /** 0102 * @brief Given a normalized position, this function returns the closest 0103 * handle to that position 0104 */ 0105 int closestHandleToPosition(qreal position) const; 0106 /** 0107 * @brief Given a widget-relative x position in pixels, this function 0108 * returns the normalized position relative to the gradient rect 0109 */ 0110 qreal positionFromX(int x) const; 0111 /** 0112 * @brief Given a widget-relative x position, this function returns the 0113 * closest handle to that position 0114 */ 0115 int closestHandleToX(int x) const; 0116 /** 0117 * @brief Given a gradient rect relative position, this function returns the 0118 * x position in pixels relative to the widget 0119 */ 0120 int xFromPosition(qreal position) const; 0121 /** 0122 * @brief Derived classes must override this function to draw the gradient 0123 * inside the given rect. A border is automatically drawn after 0124 */ 0125 virtual void paintGradient(QPainter &painter, const QRect &rect) = 0; 0126 /** 0127 * @brief Override this function to paint custom handles 0128 */ 0129 virtual void paintHandle(QPainter &painter, const QRect &rect, const Handle &handle); 0130 0131 void handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers); 0132 0133 void paintEvent(QPaintEvent *e) override; 0134 void mousePressEvent(QMouseEvent *e) override; 0135 void mouseMoveEvent(QMouseEvent *e) override; 0136 void leaveEvent(QEvent *e) override; 0137 void keyPressEvent(QKeyEvent *e) override; 0138 void wheelEvent(QWheelEvent *e) override; 0139 }; 0140 0141 /** 0142 * @brief This is a simple input levels slider that has no gamma handle. Use it 0143 * if you want to show a simple mapping or contrast adjustement. The handles 0144 * are constrained so that the black point handle can not pass the white point 0145 * handle and viceversa 0146 */ 0147 class KRITAWIDGETS_EXPORT KisInputLevelsSlider : public KisLevelsSlider 0148 { 0149 Q_OBJECT 0150 0151 public: 0152 KisInputLevelsSlider(QWidget *parent = nullptr); 0153 ~KisInputLevelsSlider(); 0154 0155 /** 0156 * @brief Get the normalized black point 0157 */ 0158 qreal blackPoint() const; 0159 /** 0160 * @brief Get the normalized white point 0161 */ 0162 qreal whitePoint() const; 0163 0164 public Q_SLOTS: 0165 /** 0166 * @brief Sets the black point 0167 */ 0168 virtual void setBlackPoint(qreal newBlackPoint); 0169 /** 0170 * @brief Sets the white point 0171 */ 0172 virtual void setWhitePoint(qreal newWhitePoint); 0173 /** 0174 * @brief Sometimes you want to set the range to a totally different place, 0175 * but the new black point can be greater than the old white point so the 0176 * new black point position will be constrained to the old white position. 0177 * This function allows to set both values at once to prevent that 0178 * undesirable effect. Use it when the widget has to show new values, for a 0179 * different set of levels parameters for example 0180 */ 0181 virtual void reset(qreal newBlackPoint, qreal newWhitePoint); 0182 0183 Q_SIGNALS: 0184 /** 0185 * @brief Signal emited when the black point changes 0186 */ 0187 void blackPointChanged(qreal newBlackPoint); 0188 /** 0189 * @brief Signal emited when the white point changes 0190 */ 0191 void whitePointChanged(qreal newWhitePoint); 0192 0193 protected: 0194 /** 0195 * @brief Custom gradient painter. This paints two bars in the gradient 0196 * rect, one on top of the other. The top one shows a simple black to white 0197 * (more exactly, color of the first handle to color of the last handle) 0198 * linear gradient. The bottom one shows black (first handle's color) from 0199 * the right side of the rect to the black point, and white (last handle's 0200 * color) from the white point to the left side of the rect. In the middle 0201 * of the two handles, the "paintBottomGradientMiddleSection" is used to 0202 * fill the bar 0203 */ 0204 void paintGradient(QPainter &painter, const QRect &rect) override; 0205 /** 0206 * @brief This is used to fill the space between the tho handles in the 0207 * bottom bar of the "gradient". It just paints a linear gradient that goes 0208 * from black (first handle's color) to white (last handle's color). Derived 0209 * classes can override this function if they only want to change that area. 0210 * "gradientImage" is a 256x1px image. 0211 */ 0212 virtual void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_); 0213 }; 0214 0215 /** 0216 * @brief This is a input levels slider that has a gamma handle. The handles 0217 * are constrained so that the black point handle can not pass the white point 0218 * handle and viceversa 0219 */ 0220 class KRITAWIDGETS_EXPORT KisInputLevelsSliderWithGamma : public KisInputLevelsSlider 0221 { 0222 Q_OBJECT 0223 0224 public: 0225 KisInputLevelsSliderWithGamma(QWidget *parent = nullptr); 0226 ~KisInputLevelsSliderWithGamma(); 0227 0228 /** 0229 * @brief Get the gamma value 0230 */ 0231 qreal gamma() const; 0232 0233 public Q_SLOTS: 0234 void setHandlePosition(int handleIndex, qreal newPosition) override; 0235 /** 0236 * @brief Sets the gamma value 0237 */ 0238 void setGamma(qreal newGamma); 0239 /** 0240 * @see KisInputLevelsSlider::reset 0241 */ 0242 void reset(qreal newBlackPoint, qreal newWhitePoint) override; 0243 /** 0244 * @see KisInputLevelsSlider::reset 0245 */ 0246 void reset(qreal newBlackPoint, qreal newWhitePoint, qreal newGamma); 0247 0248 Q_SIGNALS: 0249 /** 0250 * @brief Signal emited when the gamma value changes 0251 */ 0252 void gammaChanged(qreal newGamma); 0253 0254 protected: 0255 void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_) override; 0256 0257 private: 0258 qreal m_gamma; 0259 0260 qreal gammaToPosition() const; 0261 qreal positionToGamma() const; 0262 }; 0263 0264 /** 0265 * @brief This is a simple output levels slider. It is basically the same as 0266 * KisInputLevelsSlider but the handles are not constrained and can move freely 0267 */ 0268 class KRITAWIDGETS_EXPORT KisOutputLevelsSlider : public KisInputLevelsSlider 0269 { 0270 Q_OBJECT 0271 0272 public: 0273 KisOutputLevelsSlider(QWidget *parent = nullptr); 0274 ~KisOutputLevelsSlider(); 0275 }; 0276 0277 /** 0278 * @brief This is a threshold slider that only has one handle 0279 */ 0280 class KRITAWIDGETS_EXPORT KisThresholdSlider : public KisInputLevelsSlider 0281 { 0282 Q_OBJECT 0283 0284 public: 0285 KisThresholdSlider(QWidget *parent = nullptr); 0286 ~KisThresholdSlider(); 0287 0288 /** 0289 * @brief Get the gamma value 0290 */ 0291 qreal threshold() const; 0292 0293 public Q_SLOTS: 0294 /** 0295 * @brief Sets the black point. For this slider the black and the white 0296 * points are always in the same position, so changing the black point will 0297 * also change the white point and viceversa 0298 */ 0299 void setHandlePosition(int handleIndex, qreal newPosition) override; 0300 /** 0301 * @brief Sets the black point. For this slider the black and the white 0302 * points are always in the same position, so changing the black point will 0303 * also change the white point and viceversa 0304 */ 0305 void setBlackPoint(qreal newBlackPoint) override; 0306 /** 0307 * @brief Sets the white point. For this slider the black and the white 0308 * points are always in the same position, so changing the black point will 0309 * also change the white point and viceversa 0310 */ 0311 void setWhitePoint(qreal newWhitePoint) override; 0312 void reset(qreal newBlackPoint, qreal newWhitePoint) override; 0313 /** 0314 * @brief Sets the gamma value 0315 */ 0316 void setThreshold(qreal newGamma); 0317 0318 Q_SIGNALS: 0319 /** 0320 * @brief Signal emited when the threshold value changes 0321 */ 0322 void thresholdChanged(qreal newThreshold); 0323 0324 protected: 0325 void paintBottomGradientMiddleSection(QImage &gradientImage, const QVector<Handle> &sortedHandles_) override; 0326 void paintHandle(QPainter &painter, const QRect &rect, const Handle &handle) override; 0327 }; 0328 0329 #endif