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