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