File indexing completed on 2024-06-16 04:15:59
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Adam Celarek <kdedev at xibo dot at> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_color_selector_base.h" 0008 0009 #include <QMouseEvent> 0010 #include <QApplication> 0011 #include <QDesktopWidget> 0012 #include <QScreen> 0013 #include <QTimer> 0014 #include <QCursor> 0015 #include <QPainter> 0016 #include <QMimeData> 0017 0018 #include <kconfig.h> 0019 #include <kconfiggroup.h> 0020 #include <ksharedconfig.h> 0021 0022 #include "KoColorSpace.h" 0023 #include "KoColorSpaceRegistry.h" 0024 0025 #include "kis_canvas2.h" 0026 #include "kis_canvas_resource_provider.h" 0027 #include "kis_node.h" 0028 #include "KisViewManager.h" 0029 #include <KisView.h> 0030 #include "kis_image.h" 0031 #include "kis_global.h" 0032 #include "kis_display_color_converter.h" 0033 0034 #include <resources/KoGamutMask.h> 0035 0036 class KisColorPreviewPopup : public QWidget { 0037 public: 0038 KisColorPreviewPopup(KisColorSelectorBase* parent) 0039 : QWidget(parent), m_parent(parent) 0040 { 0041 setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); 0042 setQColor(QColor(0,0,0)); 0043 m_baseColor = QColor(0,0,0,0); 0044 m_previousColor = QColor(0,0,0,0); 0045 m_lastUsedColor = QColor(0,0,0,0); 0046 } 0047 0048 void show() 0049 { 0050 updatePosition(); 0051 QWidget::show(); 0052 } 0053 0054 void updatePosition() 0055 { 0056 QPoint parentPos = m_parent->mapToGlobal(QPoint(0,0)); 0057 const QRect availRect = QApplication::desktop()->availableGeometry(this); 0058 QPoint targetPos; 0059 if ( parentPos.x() - 100 > availRect.x() ) { 0060 targetPos = QPoint(parentPos.x() - 100, parentPos.y()); 0061 } else if ( parentPos.x() + m_parent->width() + 100 < availRect.right()) { 0062 targetPos = m_parent->mapToGlobal(QPoint(m_parent->width(), 0)); 0063 } else if ( parentPos.y() - 100 > availRect.y() ) { 0064 targetPos = QPoint(parentPos.x(), parentPos.y() - 100); 0065 } else { 0066 targetPos = QPoint(parentPos.x(), parentPos.y() + m_parent->height()); 0067 } 0068 setGeometry(targetPos.x(), targetPos.y(), 100, 150); 0069 setAttribute(Qt::WA_TranslucentBackground); 0070 } 0071 0072 void setQColor(const QColor& color) 0073 { 0074 m_color = color; 0075 update(); 0076 } 0077 0078 void setPreviousColor() 0079 { 0080 m_previousColor = m_baseColor; 0081 } 0082 0083 void setBaseColor(const QColor& color) 0084 { 0085 m_baseColor = color; 0086 update(); 0087 } 0088 0089 void setLastUsedColor(const QColor& color) 0090 { 0091 m_lastUsedColor = color; 0092 update(); 0093 } 0094 0095 protected: 0096 void paintEvent(QPaintEvent *e) override { 0097 Q_UNUSED(e); 0098 QPainter p(this); 0099 p.fillRect(0, 0, width(), width(), m_color); 0100 p.fillRect(50, width(), width(), height(), m_previousColor); 0101 p.fillRect(0, width(), 50, height(), m_lastUsedColor); 0102 } 0103 0104 void enterEvent(QEvent *e) override { 0105 QWidget::enterEvent(e); 0106 m_parent->tryHideAllPopups(); 0107 } 0108 0109 void leaveEvent(QEvent *e) override { 0110 QWidget::leaveEvent(e); 0111 m_parent->tryHideAllPopups(); 0112 } 0113 0114 private: 0115 KisColorSelectorBase* m_parent; 0116 QColor m_color; 0117 QColor m_baseColor; 0118 QColor m_previousColor; 0119 QColor m_lastUsedColor; 0120 }; 0121 0122 KisColorSelectorBase::KisColorSelectorBase(QWidget *parent) : 0123 QWidget(parent), 0124 m_canvas(0), 0125 m_popup(0), 0126 m_parent(0), 0127 m_colorUpdateAllowed(true), 0128 m_colorUpdateSelf(false), 0129 m_hideTimer(new QTimer(this)), 0130 m_popupOnMouseOver(false), 0131 m_popupOnMouseClick(true), 0132 m_colorSpace(0), 0133 m_isPopup(false), 0134 m_hideOnMouseClick(false), 0135 m_colorPreviewPopup(new KisColorPreviewPopup(this)) 0136 { 0137 m_hideTimer->setInterval(0); 0138 m_hideTimer->setSingleShot(true); 0139 connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hidePopup())); 0140 0141 using namespace std::placeholders; // For _1 placeholder 0142 auto function = std::bind(&KisColorSelectorBase::slotUpdateColorAndPreview, this, _1); 0143 m_updateColorCompressor.reset(new ColorCompressorType(20 /* ms */, function)); 0144 } 0145 0146 KisColorSelectorBase::~KisColorSelectorBase() 0147 { 0148 delete m_popup; 0149 delete m_colorPreviewPopup; 0150 } 0151 0152 void KisColorSelectorBase::setPopupBehaviour(bool onMouseOver, bool onMouseClick) 0153 { 0154 m_popupOnMouseClick = onMouseClick; 0155 m_popupOnMouseOver = onMouseOver; 0156 if(onMouseClick) { 0157 m_popupOnMouseOver = false; 0158 } 0159 0160 if(m_popupOnMouseOver) { 0161 setMouseTracking(true); 0162 } 0163 } 0164 0165 void KisColorSelectorBase::setColorSpace(const KoColorSpace *colorSpace) 0166 { 0167 m_colorSpace = colorSpace; 0168 } 0169 0170 void KisColorSelectorBase::setCanvas(KisCanvas2 *canvas) 0171 { 0172 if (m_canvas) { 0173 m_canvas->disconnectCanvasObserver(this); 0174 } 0175 m_canvas = canvas; 0176 if (m_canvas) { 0177 connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), 0178 SLOT(canvasResourceChanged(int,QVariant)), Qt::UniqueConnection); 0179 0180 connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()), 0181 SLOT(reset()), Qt::UniqueConnection); 0182 0183 connect(canvas->imageView()->resourceProvider(), SIGNAL(sigFGColorUsed(KoColor)), 0184 this, SLOT(updateLastUsedColorPreview(KoColor)), Qt::UniqueConnection); 0185 0186 if (m_canvas->viewManager() && m_canvas->viewManager()->canvasResourceProvider()) { 0187 setColor(Acs::currentColor(m_canvas->viewManager()->canvasResourceProvider(), Acs::Foreground)); 0188 } 0189 } 0190 if (m_popup) { 0191 m_popup->setCanvas(canvas); 0192 } 0193 0194 reset(); 0195 } 0196 0197 void KisColorSelectorBase::unsetCanvas() 0198 { 0199 if (m_popup) { 0200 m_popup->unsetCanvas(); 0201 } 0202 m_canvas = 0; 0203 } 0204 0205 0206 0207 void KisColorSelectorBase::mousePressEvent(QMouseEvent* event) 0208 { 0209 event->accept(); 0210 0211 if(!m_isPopup && m_popupOnMouseClick && 0212 event->button() == Qt::MiddleButton) { 0213 0214 lazyCreatePopup(); 0215 0216 int x = event->globalX(); 0217 int y = event->globalY(); 0218 int popupsize = m_popup->width(); 0219 x-=popupsize/2; 0220 y-=popupsize/2; 0221 0222 const QRect availRect = QApplication::desktop()->availableGeometry(this); 0223 0224 if(x<availRect.x()) 0225 x = availRect.x(); 0226 if(y<availRect.y()) 0227 y = availRect.y(); 0228 if(x+m_popup->width()>availRect.x()+availRect.width()) 0229 x = availRect.x()+availRect.width()-m_popup->width(); 0230 if(y+m_popup->height()>availRect.y()+availRect.height()) 0231 y = availRect.y()+availRect.height()-m_popup->height(); 0232 0233 m_colorUpdateSelf=false; 0234 m_popup->move(x, y); 0235 m_popup->setHidingTime(200); 0236 showPopup(DontMove); 0237 0238 } else if (m_isPopup && event->button() == Qt::MiddleButton) { 0239 if (m_colorPreviewPopup) { 0240 m_colorPreviewPopup->hide(); 0241 } 0242 hide(); 0243 } else { 0244 m_colorUpdateSelf=true; 0245 showColorPreview(); 0246 event->ignore(); 0247 } 0248 } 0249 0250 void KisColorSelectorBase::mouseReleaseEvent(QMouseEvent *e) { 0251 0252 Q_UNUSED(e); 0253 0254 if (e->button() == Qt::MiddleButton) { 0255 e->accept(); 0256 } else if (m_isPopup && 0257 (m_hideOnMouseClick && !m_popupOnMouseOver) && 0258 !m_hideTimer->isActive()) { 0259 if (m_colorPreviewPopup) { 0260 m_colorPreviewPopup->hide(); 0261 } 0262 hide(); 0263 } 0264 } 0265 0266 void KisColorSelectorBase::enterEvent(QEvent *e) 0267 { 0268 if (m_popup && m_popup->isVisible()) { 0269 m_popup->m_hideTimer->stop(); 0270 } 0271 0272 if (m_isPopup && m_hideTimer->isActive()) { 0273 m_hideTimer->stop(); 0274 } 0275 0276 // do not show the popup when boxed in 0277 // the configuration dialog (m_canvas == 0) 0278 0279 if (m_canvas && 0280 !m_isPopup && m_popupOnMouseOver && 0281 (!m_popup || m_popup->isHidden())) { 0282 0283 lazyCreatePopup(); 0284 0285 const QRect availRect = QApplication::desktop()->availableGeometry(this); 0286 0287 QPoint proposedTopLeft = rect().center() - m_popup->rect().center(); 0288 proposedTopLeft = mapToGlobal(proposedTopLeft); 0289 0290 QRect popupRect = QRect(proposedTopLeft, m_popup->size()); 0291 popupRect = kisEnsureInRect(popupRect, availRect); 0292 0293 m_popup->setGeometry(popupRect); 0294 m_popup->setHidingTime(200); 0295 showPopup(DontMove); 0296 } 0297 0298 QWidget::enterEvent(e); 0299 } 0300 0301 void KisColorSelectorBase::leaveEvent(QEvent *e) 0302 { 0303 tryHideAllPopups(); 0304 QWidget::leaveEvent(e); 0305 } 0306 0307 void KisColorSelectorBase::keyPressEvent(QKeyEvent *) 0308 { 0309 if (m_isPopup) { 0310 hidePopup(); 0311 } 0312 } 0313 0314 void KisColorSelectorBase::dragEnterEvent(QDragEnterEvent *e) 0315 { 0316 if(e->mimeData()->hasColor()) 0317 e->acceptProposedAction(); 0318 if(e->mimeData()->hasText() && QColor(e->mimeData()->text()).isValid()) 0319 e->acceptProposedAction(); 0320 } 0321 0322 void KisColorSelectorBase::dropEvent(QDropEvent *e) 0323 { 0324 QColor color; 0325 if(e->mimeData()->hasColor()) { 0326 color = qvariant_cast<QColor>(e->mimeData()->colorData()); 0327 } 0328 else if(e->mimeData()->hasText()) { 0329 color.setNamedColor(e->mimeData()->text()); 0330 if(!color.isValid()) 0331 return; 0332 } 0333 0334 KoColor kocolor(color , KoColorSpaceRegistry::instance()->rgb8()); 0335 updateColor(kocolor, Acs::Foreground, true); 0336 } 0337 0338 void KisColorSelectorBase::updateColor(const KoColor &color, Acs::ColorRole role, bool needsExplicitColorReset) 0339 { 0340 commitColor(color, role); 0341 0342 if (needsExplicitColorReset) { 0343 setColor(color); 0344 } 0345 } 0346 0347 void KisColorSelectorBase::requestUpdateColorAndPreview(const KoColor &color, Acs::ColorRole role) 0348 { 0349 m_updateColorCompressor->start(qMakePair(color, role)); 0350 } 0351 0352 void KisColorSelectorBase::slotUpdateColorAndPreview(QPair<KoColor, Acs::ColorRole> color) 0353 { 0354 updateColorPreview(color.first); 0355 updateColor(color.first, color.second, false); 0356 } 0357 0358 void KisColorSelectorBase::setColor(const KoColor& color) 0359 { 0360 Q_UNUSED(color); 0361 } 0362 0363 void KisColorSelectorBase::setHidingTime(int time) 0364 { 0365 KIS_ASSERT_RECOVER_NOOP(m_isPopup); 0366 0367 m_hideTimer->setInterval(time); 0368 } 0369 0370 void KisColorSelectorBase::lazyCreatePopup() 0371 { 0372 if (!m_popup) { 0373 m_popup = createPopup(); 0374 Q_ASSERT(m_popup); 0375 m_popup->setParent(this); 0376 0377 // Setting Qt::BypassWindowManagerHint will prevent 0378 // the WM from showing another taskbar entry, 0379 // but will require that we handle window activation manually 0380 m_popup->setWindowFlags(Qt::FramelessWindowHint | 0381 #if defined(Q_OS_MACOS) || defined(Q_OS_ANDROID) 0382 Qt::Popup | 0383 #else 0384 Qt::Window | 0385 #endif 0386 Qt::NoDropShadowWindowHint | 0387 Qt::BypassWindowManagerHint); 0388 m_popup->m_parent = this; 0389 m_popup->m_isPopup = true; 0390 } 0391 m_popup->setCanvas(m_canvas); 0392 m_popup->updateSettings(); 0393 } 0394 0395 void KisColorSelectorBase::showPopup(Move move) 0396 { 0397 // This slot may be called by some action, 0398 // so we need to be able to handle it 0399 lazyCreatePopup(); 0400 0401 QPoint cursorPos = QCursor::pos(); 0402 QScreen *activeScreen = 0; 0403 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) 0404 activeScreen = QGuiApplication::screenAt(cursorPos); 0405 #endif 0406 const QRect availRect = (activeScreen)? activeScreen->availableGeometry() : QApplication::desktop()->availableGeometry(this); 0407 0408 if (move == MoveToMousePosition) { 0409 m_popup->move(QPoint(cursorPos.x()-m_popup->width()/2, cursorPos.y()-m_popup->height()/2)); 0410 QRect rc = m_popup->geometry(); 0411 if (rc.x() < availRect.x()) rc.setX(availRect.x()); 0412 if (rc.y() < availRect.y()) rc.setY(availRect.y()); 0413 m_popup->setGeometry(rc); 0414 } 0415 0416 if (m_colorPreviewPopup) { 0417 m_colorPreviewPopup->hide(); 0418 } 0419 0420 m_popup->show(); 0421 m_popup->m_colorPreviewPopup->show(); 0422 } 0423 0424 void KisColorSelectorBase::hidePopup() 0425 { 0426 KIS_ASSERT_RECOVER_RETURN(m_isPopup); 0427 0428 m_colorPreviewPopup->hide(); 0429 hide(); 0430 } 0431 0432 void KisColorSelectorBase::commitColor(const KoColor& color, Acs::ColorRole role) 0433 { 0434 if (!m_canvas) 0435 return; 0436 0437 m_colorUpdateAllowed=false; 0438 0439 if (role == Acs::Foreground) 0440 m_canvas->resourceManager()->setForegroundColor(color); 0441 else 0442 m_canvas->resourceManager()->setBackgroundColor(color); 0443 0444 m_colorUpdateAllowed=true; 0445 } 0446 0447 void KisColorSelectorBase::showColorPreview() 0448 { 0449 if(m_colorPreviewPopup->isHidden()) { 0450 m_colorPreviewPopup->show(); 0451 } 0452 } 0453 0454 void KisColorSelectorBase::updateColorPreview(const KoColor &color) 0455 { 0456 m_colorPreviewPopup->setQColor(converter()->toQColor(color)); 0457 } 0458 0459 void KisColorSelectorBase::canvasResourceChanged(int key, const QVariant &v) 0460 { 0461 if (key == KoCanvasResource::ForegroundColor || key == KoCanvasResource::BackgroundColor) { 0462 KoColor realColor(v.value<KoColor>()); 0463 updateColorPreview(realColor); 0464 if (m_colorUpdateAllowed && !m_colorUpdateSelf) { 0465 setColor(realColor); 0466 } 0467 } 0468 } 0469 0470 const KoColorSpace* KisColorSelectorBase::colorSpace() const 0471 { 0472 return converter()->paintingColorSpace(); 0473 } 0474 0475 void KisColorSelectorBase::updateSettings() 0476 { 0477 if(m_popup) { 0478 m_popup->updateSettings(); 0479 } 0480 0481 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); 0482 0483 0484 int zoomSelectorOptions = (int) cfg.readEntry("zoomSelectorOptions", 0) ; 0485 if (zoomSelectorOptions == 0) { 0486 setPopupBehaviour(false, true); // middle mouse button click will open zoom selector 0487 } else if (zoomSelectorOptions == 1) { 0488 setPopupBehaviour(true, false); // move over will open the zoom selector 0489 } 0490 else 0491 { 0492 setPopupBehaviour(false, false); // do not show zoom selector 0493 } 0494 0495 0496 if(m_isPopup) { 0497 m_hideOnMouseClick = cfg.readEntry("hidePopupOnClickCheck", false); 0498 const int zoomSize = cfg.readEntry("zoomSize", 280); 0499 resize(zoomSize, zoomSize); 0500 } 0501 0502 reset(); 0503 } 0504 0505 void KisColorSelectorBase::reset() 0506 { 0507 update(); 0508 } 0509 0510 void KisColorSelectorBase::updateBaseColorPreview(const KoColor &color) 0511 { 0512 m_colorPreviewPopup->setBaseColor(converter()->toQColor(color)); 0513 } 0514 0515 void KisColorSelectorBase::updatePreviousColorPreview() 0516 { 0517 m_colorPreviewPopup->setPreviousColor(); 0518 } 0519 0520 void KisColorSelectorBase::updateLastUsedColorPreview(const KoColor &color) 0521 { 0522 m_colorPreviewPopup->setLastUsedColor(converter()->toQColor(color)); 0523 } 0524 0525 KisDisplayColorConverter* KisColorSelectorBase::converter() const 0526 { 0527 return m_canvas ? 0528 m_canvas->displayColorConverter() : 0529 KisDisplayColorConverter::dumbConverterInstance(); 0530 } 0531 0532 void KisColorSelectorBase::tryHideAllPopups() 0533 { 0534 if (m_colorPreviewPopup->isVisible()) { 0535 m_colorUpdateSelf=false; //this is for allowing advanced selector to listen to outside color-change events. 0536 m_colorPreviewPopup->hide(); 0537 } 0538 0539 if (m_popup && m_popup->isVisible()) { 0540 m_popup->m_hideTimer->start(); 0541 } 0542 0543 if (m_isPopup && !m_hideTimer->isActive()) { 0544 m_hideTimer->start(); 0545 } 0546 } 0547 0548 0549 void KisColorSelectorBase::mouseMoveEvent(QMouseEvent *event) 0550 { 0551 event->accept(); 0552 } 0553 0554 0555 void KisColorSelectorBase::changeEvent(QEvent *event) 0556 { 0557 // hide the popup when another window becomes active, e.g. due to alt+tab 0558 if(m_isPopup && event->type() == QEvent::ActivationChange && !isActiveWindow()) { 0559 hidePopup(); 0560 } 0561 0562 QWidget::changeEvent(event); 0563 } 0564 0565 void KisColorSelectorBase::showEvent(QShowEvent *event) 0566 { 0567 QWidget::showEvent(event); 0568 0569 // manual activation required due to Qt::BypassWindowManagerHint 0570 if(m_isPopup) { 0571 activateWindow(); 0572 } 0573 }