File indexing completed on 2024-12-22 04:13:17
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <QDesktopWidget> 0008 #include <QGuiApplication> 0009 #include <QApplication> 0010 #include <QScreen> 0011 #include <QColor> 0012 #include <QVBoxLayout> 0013 #include <QLabel> 0014 #include <QPushButton> 0015 #include <QWindow> 0016 #include <QTimer> 0017 0018 #include <kis_canvas2.h> 0019 0020 #include "kis_shared_ptr.h" 0021 #include "kis_icon.h" 0022 #include "kis_image.h" 0023 #include "kis_wrapped_rect.h" 0024 #include "KisDocument.h" 0025 #include "KisPart.h" 0026 #include "KisReferenceImagesLayer.h" 0027 #include "KisScreenColorSampler.h" 0028 #include "KisDlgInternalColorSelector.h" 0029 #include <KisStaticInitializer.h> 0030 0031 struct KisScreenColorSampler::Private 0032 { 0033 QPushButton *screenColorSamplerButton = 0; 0034 QLabel *lblScreenColorInfo = 0; 0035 0036 KoColor currentColor = KoColor(); 0037 KoColor beforeScreenColorSampling = KoColor(); 0038 0039 bool performRealColorSamplingOfCanvas {true}; 0040 0041 KisScreenColorSamplingEventFilter *colorSamplingEventFilter = 0; 0042 0043 QWidget *inputGrabberWidget {nullptr}; 0044 0045 #ifdef Q_OS_WIN32 0046 QTimer *updateTimer = 0; 0047 QWindow dummyTransparentWindow; 0048 #endif 0049 0050 void updateInputGrabberWidget() 0051 { 0052 inputGrabberWidget = qApp->activeWindow(); 0053 } 0054 }; 0055 0056 KisScreenColorSampler::KisScreenColorSampler(bool showInfoLabel, QWidget *parent) : KisScreenColorSamplerBase(parent), m_d(new Private) 0057 { 0058 QVBoxLayout *layout = new QVBoxLayout(this); 0059 m_d->screenColorSamplerButton = new QPushButton(); 0060 0061 m_d->screenColorSamplerButton->setMinimumHeight(25); 0062 layout->addWidget(m_d->screenColorSamplerButton); 0063 0064 if (showInfoLabel) { 0065 m_d->lblScreenColorInfo = new QLabel(QLatin1String("\n")); 0066 layout->addWidget(m_d->lblScreenColorInfo); 0067 } 0068 0069 layout->setContentsMargins(0, 0, 0, 0); 0070 0071 connect(m_d->screenColorSamplerButton, SIGNAL(clicked()), SLOT(sampleScreenColor())); 0072 0073 updateIcons(); 0074 0075 #ifdef Q_OS_WIN32 0076 m_d->updateTimer = new QTimer(this); 0077 m_d->dummyTransparentWindow.resize(1, 1); 0078 m_d->dummyTransparentWindow.setFlags(Qt::Tool | Qt::FramelessWindowHint); 0079 connect(m_d->updateTimer, SIGNAL(timeout()), SLOT(updateColorSampling())); 0080 #endif 0081 } 0082 0083 KisScreenColorSampler::~KisScreenColorSampler() 0084 { 0085 } 0086 0087 void KisScreenColorSampler::updateIcons() 0088 { 0089 m_d->screenColorSamplerButton->setIcon(kisIcon("krita_tool_color_sampler")); 0090 } 0091 0092 KoColor KisScreenColorSampler::currentColor() 0093 { 0094 return m_d->currentColor; 0095 } 0096 0097 bool KisScreenColorSampler::performRealColorSamplingOfCanvas() const 0098 { 0099 return m_d->performRealColorSamplingOfCanvas; 0100 } 0101 0102 void KisScreenColorSampler::setPerformRealColorSamplingOfCanvas(bool enable) 0103 { 0104 m_d->performRealColorSamplingOfCanvas = enable; 0105 } 0106 0107 void KisScreenColorSampler::sampleScreenColor() 0108 { 0109 m_d->updateInputGrabberWidget(); 0110 if (m_d->inputGrabberWidget == nullptr) { 0111 Q_EMIT sigNewColorSampled(currentColor()); 0112 return; 0113 } 0114 0115 if (!m_d->colorSamplingEventFilter) { 0116 m_d->colorSamplingEventFilter = new KisScreenColorSamplingEventFilter(this, this); 0117 } 0118 m_d->inputGrabberWidget->installEventFilter(m_d->colorSamplingEventFilter); 0119 // If user pushes Escape, the last color before sampling will be restored. 0120 m_d->beforeScreenColorSampling = currentColor(); 0121 m_d->inputGrabberWidget->grabMouse(Qt::CrossCursor); 0122 0123 #ifdef Q_OS_WIN32 // excludes WinCE and WinRT 0124 // On Windows mouse tracking doesn't work over other processes's windows 0125 m_d->updateTimer->start(30); 0126 0127 // HACK: Because mouse grabbing doesn't work across processes, we have to have a dummy, 0128 // invisible window to catch the mouse click, otherwise we will click whatever we clicked 0129 // and loose focus. 0130 m_d->dummyTransparentWindow.show(); 0131 #endif 0132 m_d->inputGrabberWidget->grabKeyboard(); 0133 /* With setMouseTracking(true) the desired color can be more precisely sampled, 0134 * and continuously pushing the mouse button is not necessary. 0135 */ 0136 m_d->inputGrabberWidget->setMouseTracking(true); 0137 0138 m_d->screenColorSamplerButton->setDisabled(true); 0139 0140 const QPoint globalPos = QCursor::pos(); 0141 setCurrentColor(grabScreenColor(globalPos)); 0142 updateColorLabelText(globalPos); 0143 } 0144 0145 void KisScreenColorSampler::setCurrentColor(KoColor c) 0146 { 0147 m_d->currentColor = c; 0148 } 0149 0150 KoColor KisScreenColorSampler::grabScreenColor(const QPoint &p) 0151 { 0152 // First check whether we're clicking on a Krita window for some real color sampling 0153 if (m_d->performRealColorSamplingOfCanvas) { 0154 Q_FOREACH(KisView *view, KisPart::instance()->views()) { 0155 const KisCanvas2 *canvas = view->canvasBase(); 0156 const QWidget *canvasWidget = canvas->canvasWidget(); 0157 QPoint widgetPoint = canvasWidget->mapFromGlobal(p); 0158 0159 if (canvasWidget->visibleRegion().contains(widgetPoint)) { 0160 KisImageWSP image = view->image(); 0161 0162 if (image) { 0163 QPointF imagePoint = canvas->coordinatesConverter()->widgetToImage(widgetPoint); 0164 // sample from reference images first 0165 KisSharedPtr<KisReferenceImagesLayer> referenceImageLayer = view->document()->referenceImagesLayer(); 0166 0167 if (referenceImageLayer && canvas->referenceImagesDecoration()->visible()) { 0168 QColor color = referenceImageLayer->getPixel(imagePoint); 0169 if (color.isValid()) { 0170 return KoColor(color, image->colorSpace()); 0171 } 0172 } 0173 if (image->wrapAroundModePermitted()) { 0174 imagePoint = KisWrappedRect::ptToWrappedPt(imagePoint.toPoint(), image->bounds(), image->wrapAroundModeAxis()); 0175 } 0176 KoColor sampledColor = KoColor(); 0177 image->projection()->pixel(imagePoint.x(), imagePoint.y(), &sampledColor); 0178 return sampledColor; 0179 } 0180 } 0181 } 0182 } 0183 0184 const QDesktopWidget *desktop = QApplication::desktop(); 0185 const QPixmap pixmap = QGuiApplication::screens().at(desktop->screenNumber(QApplication::activeWindow()))->grabWindow(desktop->winId(), 0186 p.x(), p.y(), 1, 1); 0187 QImage i = pixmap.toImage(); 0188 KoColor col = KoColor(); 0189 col.fromQColor(QColor::fromRgb(i.pixel(0, 0))); 0190 return col; 0191 } 0192 0193 void KisScreenColorSampler::updateColorLabelText(const QPoint &globalPos) 0194 { 0195 if (m_d->lblScreenColorInfo) { 0196 KoColor col = grabScreenColor(globalPos); 0197 QString colname = KoColor::toQString(col); 0198 QString location = QString::number(globalPos.x())+QString(", ")+QString::number(globalPos.y()); 0199 m_d->lblScreenColorInfo->setWordWrap(true); 0200 m_d->lblScreenColorInfo->setText(location+QString(": ")+colname); 0201 } 0202 } 0203 0204 bool KisScreenColorSampler::handleColorSamplingMouseMove(QMouseEvent *e) 0205 { 0206 // If the cross is visible the grabbed color will be black most of the times 0207 //cp->setCrossVisible(!cp->geometry().contains(e->pos())); 0208 0209 continueUpdateColorSampling(e->globalPos()); 0210 return true; 0211 } 0212 0213 bool KisScreenColorSampler::handleColorSamplingMouseButtonRelease(QMouseEvent *e) 0214 { 0215 setCurrentColor(grabScreenColor(e->globalPos())); 0216 Q_EMIT sigNewColorSampled(currentColor()); 0217 releaseColorSampling(); 0218 return true; 0219 } 0220 0221 bool KisScreenColorSampler::handleColorSamplingKeyPress(QKeyEvent *e) 0222 { 0223 if (e->matches(QKeySequence::Cancel)) { 0224 releaseColorSampling(); 0225 setCurrentColor(m_d->beforeScreenColorSampling); 0226 Q_EMIT sigNewColorSampled(currentColor()); 0227 } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { 0228 setCurrentColor(grabScreenColor(QCursor::pos())); 0229 Q_EMIT sigNewColorSampled(currentColor()); 0230 releaseColorSampling(); 0231 } 0232 e->accept(); 0233 return true; 0234 } 0235 0236 void KisScreenColorSampler::releaseColorSampling() 0237 { 0238 m_d->inputGrabberWidget->removeEventFilter(m_d->colorSamplingEventFilter); 0239 m_d->inputGrabberWidget->releaseMouse(); 0240 #ifdef Q_OS_WIN32 0241 m_d->updateTimer->stop(); 0242 m_d->dummyTransparentWindow.setVisible(false); 0243 #endif 0244 m_d->inputGrabberWidget->releaseKeyboard(); 0245 m_d->inputGrabberWidget->setMouseTracking(false); 0246 0247 if (m_d->lblScreenColorInfo) { 0248 m_d->lblScreenColorInfo->setText(QLatin1String("\n")); 0249 } 0250 0251 m_d->screenColorSamplerButton->setDisabled(false); 0252 } 0253 0254 void KisScreenColorSampler::changeEvent(QEvent *e) 0255 { 0256 QWidget::changeEvent(e); 0257 } 0258 0259 void KisScreenColorSampler::updateColorSampling() 0260 { 0261 static QPoint lastGlobalPos; 0262 QPoint newGlobalPos = QCursor::pos(); 0263 if (lastGlobalPos == newGlobalPos) 0264 return; 0265 lastGlobalPos = newGlobalPos; 0266 0267 if (!rect().contains(mapFromGlobal(newGlobalPos))) { // Inside the dialog mouse tracking works, handleColorSamplingMouseMove will be called 0268 continueUpdateColorSampling(newGlobalPos); 0269 #ifdef Q_OS_WIN32 0270 m_d->dummyTransparentWindow.setPosition(newGlobalPos); 0271 #endif 0272 } 0273 } 0274 0275 void KisScreenColorSampler::continueUpdateColorSampling(const QPoint &globalPos) 0276 { 0277 const KoColor color = grabScreenColor(globalPos); 0278 // QTBUG-39792, do not change standard, custom color selectors while moving as 0279 // otherwise it is not possible to pre-select a custom cell for assignment. 0280 setCurrentColor(color); 0281 Q_EMIT sigNewColorHovered(currentColor()); 0282 updateColorLabelText(globalPos); 0283 } 0284 0285 // Event filter to be installed on the dialog while in color-sampling mode. 0286 KisScreenColorSamplingEventFilter::KisScreenColorSamplingEventFilter(KisScreenColorSampler *w, QObject *parent) 0287 : QObject(parent) 0288 , m_w(w) 0289 {} 0290 0291 bool KisScreenColorSamplingEventFilter::eventFilter(QObject *, QEvent *event) 0292 { 0293 switch (event->type()) { 0294 case QEvent::MouseMove: 0295 return m_w->handleColorSamplingMouseMove(static_cast<QMouseEvent *>(event)); 0296 case QEvent::MouseButtonRelease: 0297 return m_w->handleColorSamplingMouseButtonRelease(static_cast<QMouseEvent *>(event)); 0298 case QEvent::KeyPress: 0299 return m_w->handleColorSamplingKeyPress(static_cast<QKeyEvent *>(event)); 0300 default: 0301 break; 0302 } 0303 return false; 0304 } 0305 0306 // Register the color sampler factory with the internal color selector 0307 KIS_DECLARE_STATIC_INITIALIZER { 0308 KisDlgInternalColorSelector::setScreenColorSamplerFactory(KisScreenColorSampler::createScreenColorSampler); 0309 } 0310