1 // SPDX-License-Identifier: GPL-3.0-or-later
2 // SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
3 
4 #include "sidepanelwidget.h"
5 #include "src/core/qguiappcurrentscreen.h"
6 #include "src/utils/colorutils.h"
7 #include "src/utils/pathinfo.h"
8 #include <QFormLayout>
9 #include <QKeyEvent>
10 #include <QLabel>
11 #include <QPushButton>
12 #include <QSlider>
13 #include <QVBoxLayout>
14 #if defined(Q_OS_MACOS)
15 #include <QScreen>
16 #endif
17 
18 class QColorPickingEventFilter : public QObject
19 {
20 public:
QColorPickingEventFilter(SidePanelWidget * pw,QObject * parent=nullptr)21     explicit QColorPickingEventFilter(SidePanelWidget* pw,
22                                       QObject* parent = nullptr)
23       : QObject(parent)
24       , m_pw(pw)
25     {}
26 
eventFilter(QObject *,QEvent * event)27     bool eventFilter(QObject*, QEvent* event) override
28     {
29         event->accept();
30         switch (event->type()) {
31             case QEvent::MouseMove:
32                 return m_pw->handleMouseMove(static_cast<QMouseEvent*>(event));
33             case QEvent::MouseButtonPress:
34                 return m_pw->handleMouseButtonPressed(
35                   static_cast<QMouseEvent*>(event));
36             case QEvent::KeyPress:
37                 return m_pw->handleKeyPress(static_cast<QKeyEvent*>(event));
38             default:
39                 break;
40         }
41         return false;
42     }
43 
44 private:
45     SidePanelWidget* m_pw;
46 };
47 
48 ////////////////////////
49 
SidePanelWidget(QPixmap * p,QWidget * parent)50 SidePanelWidget::SidePanelWidget(QPixmap* p, QWidget* parent)
51   : QWidget(parent)
52   , m_pixmap(p)
53   , m_eventFilter(nullptr)
54 {
55     m_layout = new QVBoxLayout(this);
56 
57     QFormLayout* colorForm = new QFormLayout();
58     m_thicknessSlider = new QSlider(Qt::Horizontal);
59     m_thicknessSlider->setRange(1, 100);
60     m_thicknessSlider->setValue(m_thickness);
61     m_colorLabel = new QLabel();
62     m_colorLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
63     colorForm->addRow(tr("Active thickness:"), m_thicknessSlider);
64     colorForm->addRow(tr("Active color:"), m_colorLabel);
65     m_layout->addLayout(colorForm);
66 
67     connect(m_thicknessSlider,
68             &QSlider::valueChanged,
69             this,
70             &SidePanelWidget::updateCurrentThickness);
71     connect(this,
72             &SidePanelWidget::thicknessChanged,
73             this,
74             &SidePanelWidget::updateThickness);
75 
76     QColor background = this->palette().window().color();
77     bool isDark = ColorUtils::colorIsDark(background);
78     QString modifier =
79       isDark ? PathInfo::whiteIconPath() : PathInfo::blackIconPath();
80     QIcon grabIcon(modifier + "colorize.svg");
81     m_colorGrabButton = new QPushButton(grabIcon, QLatin1String(""));
82     updateGrabButton(false);
83     connect(m_colorGrabButton,
84             &QPushButton::pressed,
85             this,
86             &SidePanelWidget::colorGrabberActivated);
87     m_layout->addWidget(m_colorGrabButton);
88 
89     m_colorWheel = new color_widgets::ColorWheel(this);
90     m_colorWheel->setColor(m_color);
91     connect(m_colorWheel,
92             &color_widgets::ColorWheel::mouseReleaseOnColor,
93             this,
94             &SidePanelWidget::colorChanged);
95     connect(m_colorWheel,
96             &color_widgets::ColorWheel::colorChanged,
97             this,
98             &SidePanelWidget::updateColorNoWheel);
99     m_layout->addWidget(m_colorWheel);
100 }
101 
updateColor(const QColor & c)102 void SidePanelWidget::updateColor(const QColor& c)
103 {
104     m_color = c;
105     m_colorLabel->setStyleSheet(
106       QStringLiteral("QLabel { background-color : %1; }").arg(c.name()));
107     m_colorWheel->setColor(m_color);
108 }
109 
updateThickness(const int & t)110 void SidePanelWidget::updateThickness(const int& t)
111 {
112     m_thickness = qBound(0, t, 100);
113     m_thicknessSlider->setValue(m_thickness);
114 }
115 
updateColorNoWheel(const QColor & c)116 void SidePanelWidget::updateColorNoWheel(const QColor& c)
117 {
118     m_color = c;
119     m_colorLabel->setStyleSheet(
120       QStringLiteral("QLabel { background-color : %1; }").arg(c.name()));
121 }
122 
updateCurrentThickness(int value)123 void SidePanelWidget::updateCurrentThickness(int value)
124 {
125     emit thicknessChanged(value);
126 }
127 
colorGrabberActivated()128 void SidePanelWidget::colorGrabberActivated()
129 {
130     grabKeyboard();
131     grabMouse(Qt::CrossCursor);
132     setMouseTracking(true);
133     m_colorBackup = m_color;
134     if (!m_eventFilter) {
135         m_eventFilter = new QColorPickingEventFilter(this, this);
136     }
137     installEventFilter(m_eventFilter);
138     updateGrabButton(true);
139 }
140 
releaseColorGrab()141 void SidePanelWidget::releaseColorGrab()
142 {
143     setMouseTracking(false);
144     removeEventFilter(m_eventFilter);
145     releaseMouse();
146     releaseKeyboard();
147     setFocus();
148     updateGrabButton(false);
149 }
150 
grabPixmapColor(const QPoint & p)151 QColor SidePanelWidget::grabPixmapColor(const QPoint& p)
152 {
153     QColor c;
154     if (m_pixmap) {
155 #if defined(Q_OS_MACOS)
156         QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen();
157         QPoint point = p;
158         if (currentScreen) {
159             point = QPoint((p.x() - currentScreen->geometry().x()) *
160                              currentScreen->devicePixelRatio(),
161                            (p.y() - currentScreen->geometry().y()) *
162                              currentScreen->devicePixelRatio());
163         }
164         QPixmap pixel = m_pixmap->copy(QRect(point, point));
165 #else
166         QPixmap pixel = m_pixmap->copy(QRect(p, p));
167 #endif
168         c = pixel.toImage().pixel(0, 0);
169     }
170     return c;
171 }
172 
handleKeyPress(QKeyEvent * e)173 bool SidePanelWidget::handleKeyPress(QKeyEvent* e)
174 {
175     if (e->key() == Qt::Key_Escape) {
176         releaseColorGrab();
177         updateColor(m_colorBackup);
178     } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
179         updateColor(grabPixmapColor(QCursor::pos()));
180         releaseColorGrab();
181         emit colorChanged(m_color);
182     }
183     return true;
184 }
185 
handleMouseButtonPressed(QMouseEvent * e)186 bool SidePanelWidget::handleMouseButtonPressed(QMouseEvent* e)
187 {
188     if (m_colorGrabButton->geometry().contains(e->pos()) ||
189         e->button() == Qt::RightButton) {
190         updateColorNoWheel(m_colorBackup);
191     } else if (e->button() == Qt::LeftButton) {
192         updateColor(grabPixmapColor(QCursor::pos()));
193     }
194     releaseColorGrab();
195     emit colorChanged(m_color);
196     return true;
197 }
198 
handleMouseMove(QMouseEvent * e)199 bool SidePanelWidget::handleMouseMove(QMouseEvent* e)
200 {
201     updateColorNoWheel(grabPixmapColor(e->globalPos()));
202     return true;
203 }
204 
updateGrabButton(const bool activated)205 void SidePanelWidget::updateGrabButton(const bool activated)
206 {
207     if (activated) {
208         m_colorGrabButton->setText(tr("Press ESC to cancel"));
209     } else {
210         m_colorGrabButton->setText(tr("Grab Color"));
211     }
212 }
213