File indexing completed on 2024-05-12 16:02:02
0001 /* 0002 * KDE. Krita Project. 0003 * 0004 * SPDX-FileCopyrightText: 2020 Deif Lou <ginoba@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #ifndef KISANGLESELECTOR_H 0010 #define KISANGLESELECTOR_H 0011 0012 #include <QWidget> 0013 #include <QScopedPointer> 0014 0015 #include <KisAngleGauge.h> 0016 #include <kis_double_parse_spin_box.h> 0017 0018 #include "kritawidgets_export.h" 0019 0020 // WORKAROUND 0021 // QAbstractSpinBox does some strange fixing of the value when it is out of 0022 // range and the wrapping is set to true. For example, if the range is 0023 // [-180, 180] and a value > 180 is set, then the spin box changes it to -180 0024 // (the minimum), and if a value < -180 is set then it is changed to 180 0025 // (the maximum). This subclass catches the value earlier and changes it to the 0026 // closest coterminal angle in the range 0027 class KRITAWIDGETS_EXPORT KisAngleSelectorSpinBox : public KisDoubleParseSpinBox 0028 { 0029 Q_OBJECT 0030 public: 0031 KisAngleSelectorSpinBox(QWidget *parent = 0); 0032 ~KisAngleSelectorSpinBox() override; 0033 void setRange(double min, double max); 0034 double valueFromText(const QString & text) const override; 0035 bool isFlat() const; 0036 void setFlat(bool newFlat); 0037 QSize minimumSizeHint() const override; 0038 QSize sizeHint() const override; 0039 void refreshStyle(); 0040 protected: 0041 void enterEvent(QEvent *e) override; 0042 void leaveEvent(QEvent *e) override; 0043 void focusInEvent(QFocusEvent *e) override; 0044 void focusOutEvent(QFocusEvent *e) override; 0045 private: 0046 struct Private; 0047 const QScopedPointer<Private> m_d; 0048 }; 0049 0050 /** 0051 * @brief A widget with several options to select an angle 0052 * 0053 * This widget is a combination of a @ref KisAngleGauge and a spin box, 0054 * along with some flipping options 0055 */ 0056 class KRITAWIDGETS_EXPORT KisAngleSelector : public QWidget 0057 { 0058 Q_OBJECT 0059 0060 public: 0061 /** 0062 * @brief Options to select how the flip options should be presented 0063 * @see flipOptionsMode() const 0064 * @see setFlipOptionsMode(FlipOptionsMode newMode) 0065 */ 0066 enum FlipOptionsMode 0067 { 0068 /** 0069 * @brief There is no flip options available 0070 */ 0071 FlipOptionsMode_NoFlipOptions, 0072 /** 0073 * @brief The flip options are shown as a menu accessible via a options button 0074 */ 0075 FlipOptionsMode_MenuButton, 0076 /** 0077 * @brief The flip options are shown as individual buttons 0078 */ 0079 FlipOptionsMode_Buttons, 0080 /** 0081 * @brief The flip options are shown only as a context menu when 0082 * right-clicking the gauge widget 0083 * 0084 * The options are shown in the context menu also if the mode is 0085 * FlipOptionsMode_MenuButton or FlipOptionsMode_Buttons but with this 0086 * mode there will be no additional buttons 0087 */ 0088 FlipOptionsMode_ContextMenu 0089 }; 0090 0091 /** 0092 * @brief Construct a new KisAngleSelector widget 0093 * @param parent the parent widget 0094 */ 0095 explicit KisAngleSelector(QWidget *parent = 0); 0096 ~KisAngleSelector(); 0097 0098 /** 0099 * @brief Gets the current angle 0100 * @return The current angle 0101 * @see setAngle(qreal) 0102 */ 0103 qreal angle() const; 0104 /** 0105 * @brief Gets the angle to which multiples the selected angle will snap 0106 * 0107 * The default snap angle is 15 degrees so the selected angle will snap 0108 * to its multiples (0, 15, 30, 45, etc.) 0109 * @return The angle to which multiples the selected angle will snap 0110 * @see setSnapAngle(qreal) 0111 */ 0112 qreal snapAngle() const; 0113 /** 0114 * @brief Gets the angle that is used to reset the current angle 0115 * 0116 * This angle is used when the user double clicks on the widget 0117 * @return The angle that is used to reset the current angle 0118 * @see setResetAngle(qreal) 0119 */ 0120 qreal resetAngle() const; 0121 /** 0122 * @brief Gets the number of decimals (precision) used by the angle 0123 * 0124 * If you want to simulate integer angles, set it to 0. The default is 2. 0125 * @return The number of decimals being used 0126 * @see setDecimals(int) 0127 */ 0128 int decimals() const; 0129 /** 0130 * @brief Gets the maximum value for the angle 0131 * 0132 * The default is 360 0133 * @return The maximum value for the angle 0134 * @see setMaximum(qreal) 0135 * @see setRange(qreal, qreal) 0136 */ 0137 qreal maximum() const; 0138 /** 0139 * @brief Gets the minimum value for the angle 0140 * 0141 * The default is 0 0142 * @return The minimum value for the angle 0143 * @see setMinimum(qreal) 0144 * @see setRange(qreal, qreal) 0145 */ 0146 qreal minimum() const; 0147 /** 0148 * @brief Gets the prefix shown in the spin box 0149 * @return The prefix shown in the spin box 0150 * @see setPrefix(const QString&) 0151 */ 0152 QString prefix() const; 0153 /** 0154 * @brief Gets if the angle should wrap pass the minimum or maximum angles 0155 * @return True if the angle should wrap pass the minimum or maximum angles, 0156 * false otherwise 0157 * @see setWrapping(bool) 0158 */ 0159 bool wrapping() const; 0160 /** 0161 * @brief Gets the mode in which the flip options should be shown 0162 * 0163 * The default is FlipOptions::FlipOptionsMode_Buttons 0164 * @return The mode in which the flip options should be shown. 0165 * @see setFlipOptionsMode(FlipOptionsMode) 0166 * @see FlipOptionsMode 0167 */ 0168 FlipOptionsMode flipOptionsMode() const; 0169 /** 0170 * @brief Gets the size of the gauge widget 0171 * 0172 * By default the size of the gauge is set to the height of the spin box 0173 * @return The size of the gauge widget 0174 * @see setGaugeSize(int) 0175 */ 0176 int gaugeSize() const; 0177 /** 0178 * @brief Gets the direction in which the angle increases in the angle gauge 0179 * @return The direction in which the angle increases 0180 * @see KisAngleGauge::IcreasingDirection 0181 * @see setIncreasingDirection(KisAngleGauge::IcreasingDirection) 0182 */ 0183 KisAngleGauge::IncreasingDirection increasingDirection() const; 0184 /** 0185 * @brief Gets if the spin box is flat (no border or background) 0186 * @return True if the spin box is flat, false otherwise 0187 * @see useFlatSpinBox(bool) 0188 */ 0189 bool isUsingFlatSpinBox() const; 0190 0191 /** 0192 * @brief Sets the angle to which multiples the selected angle will snap 0193 * @param newSnapAngle the new angle to which multiples the selected angle will snap 0194 * @see snapAngle() const 0195 */ 0196 void setSnapAngle(qreal newSnapAngle); 0197 /** 0198 * @brief Sets the angle that is used to reset the current angle 0199 * @param newResetAngle the new angle that is used to reset the current angle 0200 * @see resetAngle() const 0201 */ 0202 void setResetAngle(qreal newResetAngle); 0203 /** 0204 * @brief Sets the number of decimals (precision) used by the angle 0205 * @param newNumberOfDecimals the new number of decimals used by the angle 0206 * @see decimals() const 0207 */ 0208 void setDecimals(int newNumberOfDecimals); 0209 /** 0210 * @brief Sets the maximum value for the angle 0211 * @param newMaximum the new maximum value for the angle 0212 * @see maximum() const 0213 * @see setRange(qreal, qreal) 0214 */ 0215 void setMaximum(qreal newMaximum); 0216 /** 0217 * @brief Sets the minimum value for the angle 0218 * @param newMinimum the new minimum value for the angle 0219 * @see minimum() const 0220 * @see setRange(qreal, qreal) 0221 */ 0222 void setMinimum(qreal newMinimum); 0223 /** 0224 * @brief Sets the minimum and maximum values for the angle 0225 * @param newMinimum the new minimum value for the angle 0226 * @param newMaximum the new maximum value for the angle 0227 * @see minimum() const 0228 * @see maximum() const 0229 * @see setMinimum(qreal) 0230 * @see setMaximum(qreal) 0231 */ 0232 void setRange(qreal newMinimum, qreal newMaximum); 0233 /** 0234 * @brief Sets the prefix shown in the spin box 0235 * @param newPrefix the new prefix for the spin box 0236 * @see prefix() const 0237 */ 0238 void setPrefix(const QString &newPrefix); 0239 /** 0240 * @brief Sets if the angle should wrap pass the minimum or maximum angles 0241 * @param newWrapping true if the angle should wrap pass the minimum or 0242 * maximum angles, false otherwise 0243 * @see wrapping() const 0244 */ 0245 void setWrapping(bool newWrapping); 0246 /** 0247 * @brief Sets the mode in which the flip options should be shown 0248 * @param newMinimum the new mode in which the flip options should be shown 0249 * @see flipOptionsMode() const 0250 * @see FlipOptionsMode 0251 */ 0252 void setFlipOptionsMode(FlipOptionsMode newMode); 0253 /** 0254 * @brief Sets the size of the gauge widget 0255 * @param newGaugeSize the new size of the gauge widget 0256 * @see gaugeSize() const 0257 */ 0258 void setGaugeSize(int newGaugeSize); 0259 /** 0260 * @brief Sets the increasing direction in the angle gauge 0261 * @param newIncreasingDirection The new increasing direction 0262 * @see IcreasingDirection 0263 * @see increasingDirection() const 0264 */ 0265 void setIncreasingDirection(KisAngleGauge::IncreasingDirection newIncreasingDirection); 0266 /** 0267 * @brief Sets if the spin box should be flat 0268 * @param newUseFlatSpinBox True if the spin box should be flat, 0269 * false otherwise 0270 * @see isUsingFlatSpinBox() const 0271 */ 0272 void useFlatSpinBox(bool newUseFlatSpinBox); 0273 0274 /** 0275 * @brief Gets the closest coterminal angle to the provided angle 0276 * that is in the range provided 0277 * 0278 * A coterminal angle to the provided angle is one that differs 0279 * in size by an integer multiple of a turn (360 degrees) 0280 * @param angle The reference angle for which the function will try to 0281 * find a coterminal angle 0282 * @param minimum The range's lower bound 0283 * @param maximum The range's upper bound 0284 * @param[out] ok This parameter will be set to true if a coterminal 0285 * angle exists in the provided range, or to false otherwise 0286 * @return The closest coterminal angle in the provided range if one exists, 0287 * or the closest value in the range (the minimum or maximum) otherwise. 0288 * If the reference angle is already in the range then it is returned 0289 */ 0290 static qreal closestCoterminalAngleInRange(qreal angle, qreal minimum, qreal maximum, bool *ok = nullptr); 0291 /** 0292 * @brief Gets the closest coterminal angle to the provided angle 0293 * that is in the range established 0294 * 0295 * A coterminal angle to the provided angle is one that differs 0296 * in size by an integer multiple of a turn (360 degrees) 0297 * @param angle The reference angle for which the function will try to 0298 * find a coterminal angle 0299 * @param[out] ok This parameter will be set to true if a coterminal 0300 * angle exists in the specified range, or to false otherwise 0301 * @return The closest coterminal angle in the specified range if one exists, 0302 * or the closest value in the range (the minimum or maximum) otherwise. 0303 * If the reference angle is already in the range then it is returned 0304 */ 0305 qreal closestCoterminalAngleInRange(qreal angle, bool *ok = nullptr) const; 0306 /** 0307 * @brief Flips an angle horizontally, vertically, or both 0308 * 0309 * This function will always try to get the closest angle to the 0310 * provided one that satisfies the flipping requirements 0311 * @param angle The angle to be flipped 0312 * @param orientations Flags indicating in which directions the angle should 0313 * be flipped 0314 * @return The flipped angle 0315 */ 0316 static qreal flipAngle(qreal angle, Qt::Orientations orientations); 0317 /** 0318 * @brief Flips an angle horizontally, vertically, or both 0319 * 0320 * This function will always try to get the closest angle to the 0321 * provided one that satisfies the flipping requirements 0322 * @param angle The angle to be flipped 0323 * @param minimum The lower bound of the valid range 0324 * @param maximum The upper bound of the valid range 0325 * @param orientations Flags indicating in which directions the angle should 0326 * be flipped 0327 * @param[out] ok This parameter will be set to true if the flipped 0328 * angle is in the provided range, or to false otherwise 0329 * @return The flipped angle if it lies in the provided range or the 0330 * closest value in the range (the minimum or maximum) otherwise 0331 */ 0332 static qreal flipAngle(qreal angle, qreal minimum, qreal maximum, Qt::Orientations orientations, bool *ok = nullptr); 0333 /** 0334 * @brief Flips the angle horizontally, vertically, or both 0335 * 0336 * This function will always try to set the closest angle to the 0337 * stablished one that satisfies the flipping requirements 0338 * @param orientations Flags indicating in which directions the angle should 0339 * be flipped 0340 */ 0341 void flip(Qt::Orientations orientations); 0342 0343 public Q_SLOTS: 0344 /** 0345 * @brief Sets the current angle 0346 * @param newAngle the new angle 0347 * @see angle() const 0348 */ 0349 void setAngle(qreal newAngle); 0350 /** 0351 * @brief Sets the current angle to the reset angle 0352 * @see resetAngle() const 0353 * @see setResetAngle(qreal) const 0354 */ 0355 void reset(); 0356 0357 Q_SIGNALS: 0358 void angleChanged(qreal angle); 0359 0360 private: 0361 struct Private; 0362 const QScopedPointer<Private> m_d; 0363 0364 bool event(QEvent *e) override; 0365 bool eventFilter(QObject *o, QEvent *e) override; 0366 }; 0367 0368 #endif