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

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef COLORDIALOG_P_H
0005 #define COLORDIALOG_P_H
0006 
0007 // Include the header of the public class of this private implementation.
0008 #include "colordialog.h"
0009 
0010 #include "constpropagatingrawpointer.h"
0011 #include "genericcolor.h"
0012 #include "helper.h"
0013 #include "helperconversion.h"
0014 #include "languagechangeeventfilter.h"
0015 #include "perceptualsettings.h"
0016 #include "rgbcolor.h"
0017 #include <lcms2.h>
0018 #include <optional>
0019 #include <qbytearray.h>
0020 #include <qcolor.h>
0021 #include <qglobal.h>
0022 #include <qhash.h>
0023 #include <qobject.h>
0024 #include <qpointer.h>
0025 #include <qsharedpointer.h>
0026 #include <qstring.h>
0027 #include <qstringliteral.h>
0028 class QAction;
0029 class QDialogButtonBox;
0030 class QDoubleSpinBox;
0031 class QGroupBox;
0032 class QHBoxLayout;
0033 class QLabel;
0034 class QLineEdit;
0035 class QPushButton;
0036 class QShortcut;
0037 class QTabWidget;
0038 class QToolButton;
0039 class QWidget;
0040 
0041 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0042 #include <qtmetamacros.h>
0043 #else
0044 #include <qobjectdefs.h>
0045 #endif
0046 
0047 namespace PerceptualColor
0048 {
0049 class ChromaHueDiagram;
0050 class ColorPatch;
0051 class GradientSlider;
0052 class MultiSpinBox;
0053 class SwatchBook;
0054 class RgbColorSpace;
0055 class WheelColorPicker;
0056 
0057 /** @internal
0058  *
0059  *  @brief Private implementation within the <em>Pointer to
0060  *  implementation</em> idiom */
0061 class ColorDialogPrivate final : public QObject
0062 {
0063     Q_OBJECT
0064 public:
0065     explicit ColorDialogPrivate(ColorDialog *backLink);
0066     /** @brief Default destructor
0067      *
0068      * The destructor is non-<tt>virtual</tt> because
0069      * the class as a whole is <tt>final</tt>. */
0070     virtual ~ColorDialogPrivate() noexcept override = default;
0071 
0072     /** @brief @ref GradientSlider widget for the alpha channel. */
0073     QPointer<GradientSlider> m_alphaGradientSlider;
0074     /** @brief Pointer to the QLabel for the alpha value.
0075      *
0076      * We store this in a pointer to allow toggle the visibility later. */
0077     QPointer<QLabel> m_alphaLabel;
0078     /** @brief Spin box for the alpha channel.
0079      *
0080      * This spin box shows always the value of @ref m_alphaGradientSlider.
0081      *
0082      * @note It’s value is not set directly, but is updated via signals from
0083      * @ref m_alphaGradientSlider. Do not use it directly! */
0084     QPointer<QDoubleSpinBox> m_alphaSpinBox;
0085     /** @brief Pointer to the QButtonBox of this dialog.
0086      *
0087      * We store this in a pointer
0088      * to allow toggle the visibility later. */
0089     QPointer<QDialogButtonBox> m_buttonBox;
0090     /** @brief Pointer to the “Cancel” button of @ref m_buttonBox. */
0091     QPointer<QPushButton> m_buttonCancel;
0092     /** @brief Pointer to the “Ok” button of @ref m_buttonBox. */
0093     QPointer<QPushButton> m_buttonOK;
0094     /** @brief Pointer to the @ref ChromaHueDiagram. */
0095     QPointer<ChromaHueDiagram> m_chromaHueDiagram;
0096     /** @brief Pointer to the @ref MultiSpinBox for CIEHLC. */
0097     QPointer<MultiSpinBox> m_ciehlcD50SpinBox;
0098     /** @brief Pointer to the gamut action for @ref m_ciehlcD50SpinBox. */
0099     QPointer<QAction> m_ciehlcD50SpinBoxGamutAction;
0100     /** @brief Pointer to the label for @ref m_ciehlcD50SpinBox. */
0101     QPointer<QLabel> m_ciehlcD50SpinBoxLabel;
0102     /** @brief Pointer to the @ref ColorPatch widget. */
0103     QPointer<ColorPatch> m_colorPatch;
0104     /** @brief Holds the currently used icon theme.
0105      *
0106      * Initially this is <tt>std::nullopt</tt>. Once @ref reloadIcons() has
0107      * been called, it has an actual value. */
0108     std::optional<ColorSchemeType> m_currentIconThemeType = std::nullopt;
0109     /** @brief Current color without alpha information
0110      *
0111      * Holds the color in absolutely defined color models.
0112      *
0113      * @note This value is considered in-gamut (even thought @ref RgbColor
0114      * might tell different because of rounding errors).
0115      *
0116      * @sa @ref ColorDialog::currentColor()
0117      * @sa @ref m_currentOpaqueColorRgb */
0118     QHash<ColorModel, GenericColor> m_currentOpaqueColorAbs;
0119     /** @brief Current color without alpha information
0120      *
0121      * Holds the color in the RGB color model and derived color models.
0122      *
0123      * @note This value is in-gamut by definition.
0124      *
0125      * @sa @ref ColorDialog::currentColor()
0126      * @sa @ref m_currentOpaqueColorAbs */
0127     RgbColor m_currentOpaqueColorRgb;
0128     /** @brief If @ref q_pointer has ever been shown. */
0129     bool everShown = false;
0130     /** @brief Pointer to the @ref MultiSpinBox for HSL. */
0131     QPointer<MultiSpinBox> m_hslSpinBox;
0132     /** @brief Pointer to the label for @ref m_hslSpinBox. */
0133     QPointer<QLabel> m_hslSpinBoxLabel;
0134     /** @brief Pointer to the @ref MultiSpinBox for HSV. */
0135     QPointer<MultiSpinBox> m_hsvSpinBox;
0136     /** @brief Pointer to the label for @ref m_hsvSpinBox. */
0137     QPointer<QLabel> m_hsvSpinBoxLabel;
0138     /** @brief Pointer to the @ref MultiSpinBox for HWB. */
0139     QPointer<MultiSpinBox> m_hwbSpinBox;
0140     /** @brief Pointer to the label for @ref m_hwbSpinBox. */
0141     QPointer<QLabel> m_hwbSpinBoxLabel;
0142     /** @brief Shortcut to show the tab with @ref m_hueFirstWrapperWidget. */
0143     QPointer<QShortcut> m_hueFirstTabShortcut;
0144     /** @brief Pointer to the QWidget wrapper that contains
0145      * @ref m_wheelColorPicker. */
0146     QPointer<QWidget> m_hueFirstWrapperWidget;
0147     /** @brief Holds whether currently a color change is ongoing, or not.
0148      *
0149      * Used to avoid infinite recursions when updating the different widgets
0150      * within this dialog.
0151      * @sa @ref setCurrentOpaqueColor() */
0152     bool m_isColorChangeInProgress = false;
0153     /** @brief Holds whether the current text of @ref m_rgbLineEdit differs
0154      * from the value in @ref m_currentOpaqueColorRgb.
0155      * @sa @ref readRgbHexValues
0156      * @sa @ref updateRgbHexButBlockSignals */
0157     bool m_isDirtyRgbLineEdit = false;
0158     /** @brief An event filter used for some child widgets. */
0159     LanguageChangeEventFilter m_languageChangeEventFilter;
0160     /** @brief Internal storage for property
0161      * @ref ColorDialog::layoutDimensions */
0162     PerceptualColor::ColorDialog::DialogLayoutDimensions m_layoutDimensions =
0163         //! [layoutDimensionsDefaultValue]
0164         ColorDialog::DialogLayoutDimensions::Collapsed
0165         //! [layoutDimensionsDefaultValue]
0166         ;
0167     /** @brief The <em>effective</em> layout dimensions.
0168      *
0169      * The property @ref ColorDialog::layoutDimensions has a value
0170      * @ref ColorDialog::DialogLayoutDimensions::ScreenSizeDependent.
0171      * <em>This</em> variable holds whatever <em>effectively</em>
0172      * is applied. So it can only have the values
0173      * @ref ColorDialog::DialogLayoutDimensions::Collapsed or
0174      * @ref ColorDialog::DialogLayoutDimensions::Expanded. */
0175     PerceptualColor::ColorDialog::DialogLayoutDimensions m_layoutDimensionsEffective = m_layoutDimensions;
0176     /** @brief Shortcut to show the tab with @ref m_lightnessFirstWrapperWidget. */
0177     QPointer<QShortcut> m_lightnessFirstTabShortcut;
0178     /** @brief Pointer to the QWidget wrapper that contains
0179      * @ref m_lchLightnessSelector and @ref m_chromaHueDiagram. */
0180     QPointer<QWidget> m_lightnessFirstWrapperWidget;
0181     /** @brief Pointer to the @ref GradientSlider for LCH lightness. */
0182     QPointer<GradientSlider> m_lchLightnessSelector;
0183     /** @brief Holds the receiver slot (if any) to be disconnected
0184      *  automatically after closing the dialog.
0185      *
0186      * Its value is only meaningful if
0187      * @ref m_receiverToBeDisconnected is not null.
0188      * @sa @ref m_receiverToBeDisconnected
0189      * @sa @ref ColorDialog::open() */
0190     QByteArray m_memberToBeDisconnected;
0191     /** @brief String that is used as separator between two sections
0192      * within a @ref MultiSpinBox.
0193      *
0194      * This string is introduced <em>twice</em> between two sections
0195      * within a @ref MultiSpinBox. */
0196     static inline const QString m_multispinboxSectionSeparator = QStringLiteral(u" ");
0197     /** @brief Shortcut to show the tab with @ref m_numericalWidget. */
0198     QPointer<QShortcut> m_numericalTabShortcut;
0199     /** @brief Pointer to the widget that holds the numeric color
0200      *         representation. */
0201     QPointer<QWidget> m_numericalWidget;
0202     /** @brief Pointer to the @ref MultiSpinBox for CIEHLC. */
0203     QPointer<MultiSpinBox> m_oklchSpinBox;
0204     /** @brief Pointer to the gamut action for @ref m_oklchSpinBox. */
0205     QPointer<QAction> m_oklchSpinBoxGamutAction;
0206     /** @brief Pointer to the label for @ref m_oklchSpinBox. */
0207     QPointer<QLabel> m_oklchSpinBoxLabel;
0208     /** @brief Pointer to the basic colors widget. */
0209     QPointer<PerceptualColor::SwatchBook> m_swatchBookBasicColors;
0210     /** @brief Shortcut to show the tab with @ref m_swatchBookWrapperWidget. */
0211     QPointer<QShortcut> m_swatchBookTabShortcut;
0212     /** @brief Pointer to the QWidget wrapper that contains
0213      * the swatch books. */
0214     QPointer<QWidget> m_swatchBookWrapperWidget;
0215     /** @brief Holds the receiver object (if any) to be disconnected
0216      *  automatically after closing the dialog.
0217      *
0218      * @sa @ref m_memberToBeDisconnected
0219      * @sa @ref ColorDialog::open() */
0220     QPointer<QObject> m_receiverToBeDisconnected;
0221     /** @brief Internal storage for property @ref ColorDialog::options */
0222     ColorDialog::ColorDialogOptions m_options;
0223     /** @brief Pointer to the RgbColorSpace object. */
0224     QSharedPointer<RgbColorSpace> m_rgbColorSpace;
0225     /** @brief Group box that contains all RGB widgets and all widget for
0226      * color spaces that are defined with RGB as base (HSV, Hex…). */
0227     QPointer<QGroupBox> m_rgbGroupBox;
0228     /** @brief Pointer to the QLineEdit that represents the hexadecimal
0229      *  RGB value. */
0230     QPointer<QLineEdit> m_rgbLineEdit;
0231     /** @brief Pointer to the label for @ref m_rgbLineEdit. */
0232     QPointer<QLabel> m_rgbLineEditLabel;
0233     /** @brief Pointer to the @ref MultiSpinBox for RGB. */
0234     QPointer<MultiSpinBox> m_rgbSpinBox;
0235     /** @brief Pointer to the label for @ref m_rgbSpinBox. */
0236     QPointer<QLabel> m_rgbSpinBoxLabel;
0237     /** @brief Internal storage for @ref ColorDialog::selectedColor(). */
0238     QColor m_selectedColor;
0239     /** @brief Layout that holds the graphical and numeric selectors. */
0240     QPointer<QHBoxLayout> m_selectorLayout;
0241     /** @brief Access to the @ref Settings singleton. */
0242     PerceptualSettings &m_settings = PerceptualSettings::instance();
0243     /** @brief Button that allows to pick with the mouse a color somewhere
0244      * from the screen. */
0245     QPointer<QToolButton> m_screenColorPickerButton;
0246     /** @brief A row with two columns within a table in Qt’s rich text
0247      * formatting.
0248      *
0249      * To use it, call QString::arg() twice: Once with the content of the
0250      * first column and once with the content of the second column. */
0251     const QString tableRow = QStringLiteral(u"<tr><td>%1</td><td>%2</td></tr>");
0252     /** @brief Table assigning to each tab a value for the @ref Settings.
0253      *
0254      * This helps to convert from QString values stored in @ref Settings
0255      * to the actual tab widgets and vice versa. */
0256     QHash<QPointer<QWidget> *, QString> m_tabTable;
0257     /** @brief Pointer to the tab widget. */
0258     QPointer<QTabWidget> m_tabWidget;
0259     /** @brief @ref m_wcsBasicColors for @ref m_rgbColorSpace. */
0260     Array2D<QColor> m_wcsBasicColors;
0261     /** @brief A default color within @ref m_wcsBasicColors.
0262      *
0263      * Choosing the blue tone (no tint, no shade). Arguments in favor:
0264      *
0265      * - Blue seems to be harmonious and integrate well in many designs.
0266      * - The blue color is quite chromatic, giving a vivid impression.
0267      * - Blue does not “screem” like red.
0268      * - Blue is exactly at the middle of the swatch book.
0269      * - The tone (no tint, no shade)  is exactly at the middle of the
0270      *   swatch book. */
0271     QColor m_wcsBasicDefaultColor;
0272     /** @brief Pointer to the @ref WheelColorPicker widget. */
0273     QPointer<WheelColorPicker> m_wheelColorPicker;
0274 
0275     /** @brief Number of decimals to for most values.
0276      *
0277      * @sa @ref okdecimals */
0278     static constexpr quint8 decimals = 0;
0279     /** @brief Number of decimals to use for the Oklab/Oklch values
0280      * L, C, a, b (but not for h!).
0281      *
0282      * @sa @ref decimals */
0283     static constexpr quint8 okdecimals = decimals + 2;
0284 
0285     void applyLayoutDimensions();
0286     void initialize(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace);
0287     [[nodiscard]] QWidget *initializeNumericPage();
0288     void initializeScreenColorPicker();
0289     [[nodiscard]] QString translateColorModel(cmsColorSpaceSignature model);
0290 
0291 public Q_SLOTS:
0292     void readChromaHueDiagramValue();
0293     void readColorPatchValue();
0294     void readHlcNumericValues();
0295     void readHslNumericValues();
0296     void readHsvNumericValues();
0297     void readHwbNumericValues();
0298     void readLightnessValue();
0299     void readOklchNumericValues();
0300     void readRgbHexValues();
0301     void readRgbNumericValues();
0302     void readSwatchBookBasicColorsValue();
0303     void readWheelColorPickerValues();
0304     void reloadIcons();
0305     void retranslateUi();
0306     void saveCurrentTab();
0307     void setCurrentOpaqueColor(const QHash<PerceptualColor::ColorModel, PerceptualColor::GenericColor> &abs, QWidget *const ignoreWidget);
0308     void setCurrentOpaqueColor(const PerceptualColor::RgbColor &rgb, QWidget *const ignoreWidget);
0309     void setCurrentOpaqueColor(const QHash<PerceptualColor::ColorModel, PerceptualColor::GenericColor> &abs,
0310                                const PerceptualColor::RgbColor &rgb,
0311                                QWidget *const ignoreWidget);
0312     void updateColorPatch();
0313     void updateHlcButBlockSignals();
0314     void updateOklchButBlockSignals();
0315     void updateRgbHexButBlockSignals();
0316 
0317 private:
0318     Q_DISABLE_COPY(ColorDialogPrivate)
0319 
0320     /** @brief Pointer to the object from which <em>this</em> object
0321      *  is the private implementation. */
0322     ConstPropagatingRawPointer<ColorDialog> q_pointer;
0323 };
0324 
0325 } // namespace PerceptualColor
0326 
0327 #endif // COLORDIALOG_P_H