File indexing completed on 2024-10-13 04:16:24

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef SCREENCOLORPICKER
0005 #define SCREENCOLORPICKER
0006 
0007 #include <optional>
0008 #include <qglobal.h>
0009 #include <qpointer.h>
0010 #include <qstring.h>
0011 #include <qwidget.h>
0012 class QColorDialog;
0013 class QPushButton;
0014 
0015 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0016 #include <qcontainerfwd.h>
0017 #include <qtmetamacros.h>
0018 #else
0019 #include <qmetatype.h>
0020 #include <qobjectdefs.h>
0021 class QObject;
0022 #endif
0023 
0024 namespace PerceptualColor
0025 {
0026 
0027 /** @internal
0028  *
0029  * @brief Pick a color from the screen.
0030  *
0031  * Provides an interface to let the user pick a color from the screen.
0032  *
0033  * This feature is not available on all platforms. Use @ref isAvailable()
0034  * to check it.
0035  *
0036  * @warning On some platforms, QColorDialog is used to perform the color
0037  * picking. This might mix up the default button setting of the parent dialog.
0038  * Workaround: If using default buttons in a parent dialog, reimplement
0039  * <tt>QWidget::setVisible()</tt> in this parent dialog: Call the
0040  * parent’s class implementation, and <em>after</em> that, call
0041  * <tt>QPushButton::setDefault(true)</tt> on the default button. */
0042 class ScreenColorPicker : public QWidget
0043 {
0044     Q_OBJECT
0045 
0046 public:
0047     explicit ScreenColorPicker(QWidget *parent);
0048     virtual ~ScreenColorPicker() override;
0049     [[nodiscard]] bool isAvailable();
0050 
0051 public Q_SLOTS:
0052     void startPicking(quint8 previousColorRed, quint8 previousColorGreen, quint8 previousColorBlue);
0053 
0054 Q_SIGNALS:
0055     /** @brief A new color.
0056      *
0057      * Emitted when the user has clicked on the screen to select a new color.
0058      *
0059      * @note On some platforms, furthermore this signal is also emitted while
0060      * the user hovers over the screen with the mouse. Than, if the user
0061      * cancels with the ESC key, a new signal is emitted with the old color
0062      * passed originally to @ref startPicking.
0063      *
0064      * @param red The <em>red</em> component of the new color.
0065      *            Range: <tt>[0, 255]</tt>
0066      * @param green Same for green.
0067      * @param blue Same for blue.
0068      *
0069      * @internal
0070      *
0071      * @note This signal uses integers with the range <tt>[0, 255]</tt> as
0072      * return values because this is the maximum precision of the underlying
0073      * implementation: The QColorDialog implementation rounds on this
0074      * precision when the user pushes the ESC key, even if the previous
0075      * value was more exact. */
0076     // Choosing thre “double” values as return type, as it makes clear
0077     // what data type returns and as “Portal” actually provides
0078     // double-precision in its return values.
0079     void newColor(double red, double green, double blue);
0080 
0081 private:
0082     /** @internal @brief Only for unit tests. */
0083     friend class TestScreenColorPicker;
0084 
0085     void pickWithPortal();
0086     [[nodiscard]] static bool hasPortalSupport();
0087     void initializeQColorDialogSupport();
0088     [[nodiscard]] static bool queryPortalSupport();
0089     [[nodiscard]] static QString translateViaQColorDialog(const char *sourceText);
0090     /** @brief If on the current platform there is support for
0091      * QColorDialog-based screen color picking.
0092      *
0093      * Might hold an empty value if @ref initializeQColorDialogSupport has
0094      * never been called.
0095      *
0096      * @warning The declaration as <tt>static in‍line</tt> can be problematic:
0097      * At least when linking on MSVC against a shared/static library,
0098      * apparently there are two instances of this variable: One that is used
0099      * within the shared/dynamic library and another one that is used within
0100      * the executable that links against this library. While on GCC and Clang
0101      * this does not happen, maybe this behaviour is implementation-defined.
0102      * And we do not want to rely on implementation-defined behaviour. However,
0103      * because the variable is <tt>private</tt>, this won't make any problems
0104      * under normal circumstances, because it's inaccessible anyway. Only when
0105      * doing a whitebox test and bypass the private access modifier via the
0106      * @ref ScreenColorPicker::TestScreenColorPicker "friend declaration" for
0107      * unit tests, you might see the wrong variable and consequently possibly
0108      * the wrong value. Therefore, unit tests should only access this variable
0109      * when building against the static library. */
0110     static inline std::optional<bool> m_hasQColorDialogSupport = std::nullopt;
0111     /** @brief The hidden QColorDialog widget (if any).
0112      *
0113      * @sa @ref initializeQColorDialogSupport */
0114     QPointer<QColorDialog> m_qColorDialog;
0115     /** @brief The screen-color-picker button of the hidden QColorDialog widget
0116      * (if any).
0117      *
0118      * @sa @ref initializeQColorDialogSupport */
0119     QPointer<QPushButton> m_qColorDialogScreenButton;
0120 
0121 private Q_SLOTS:
0122     void getPortalResponse(uint exitCode, const QVariantMap &responseArguments);
0123 };
0124 
0125 } // namespace PerceptualColor
0126 
0127 #endif // SCREENCOLORPICKER