File indexing completed on 2025-02-02 14:19:58
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org> 0004 SPDX-FileCopyrightText: 1999 Cristian Tibirna <ctibirna@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "kcolorbutton.h" 0010 0011 #include <QApplication> 0012 #include <QClipboard> 0013 #include <QColorDialog> 0014 #include <QDrag> 0015 #include <QMimeData> 0016 #include <QMouseEvent> 0017 #include <QPainter> 0018 #include <QPointer> 0019 #include <QStyle> 0020 #include <QStyleOptionButton> 0021 #include <qdrawutil.h> 0022 0023 class KColorButtonPrivate 0024 { 0025 public: 0026 KColorButtonPrivate(KColorButton *qq); 0027 0028 void chooseColor(); 0029 void colorChosen(); 0030 0031 KColorButton *q; 0032 QColor m_defaultColor; 0033 bool m_bdefaultColor : 1; 0034 bool m_alphaChannel : 1; 0035 0036 QColor col; 0037 QPoint mPos; 0038 0039 QPointer<QColorDialog> dialogPtr; 0040 0041 void initStyleOption(QStyleOptionButton *opt) const; 0042 }; 0043 0044 ///////////////////////////////////////////////////////////////////// 0045 // Functions duplicated from KColorMimeData 0046 // Should be kept in sync 0047 void populateMimeData(QMimeData *mimeData, const QColor &color) 0048 { 0049 mimeData->setColorData(color); 0050 mimeData->setText(color.name()); 0051 } 0052 0053 bool canDecode(const QMimeData *mimeData) 0054 { 0055 if (mimeData->hasColor()) { 0056 return true; 0057 } 0058 if (mimeData->hasText()) { 0059 const QString colorName = mimeData->text(); 0060 if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) { 0061 return true; 0062 } 0063 } 0064 return false; 0065 } 0066 0067 QColor fromMimeData(const QMimeData *mimeData) 0068 { 0069 if (mimeData->hasColor()) { 0070 return mimeData->colorData().value<QColor>(); 0071 } 0072 if (canDecode(mimeData)) { 0073 return QColor(mimeData->text()); 0074 } 0075 return QColor(); 0076 } 0077 0078 QDrag *createDrag(const QColor &color, QObject *dragsource) 0079 { 0080 QDrag *drag = new QDrag(dragsource); 0081 QMimeData *mime = new QMimeData; 0082 populateMimeData(mime, color); 0083 drag->setMimeData(mime); 0084 QPixmap colorpix(25, 20); 0085 colorpix.fill(color); 0086 QPainter p(&colorpix); 0087 p.setPen(Qt::black); 0088 p.drawRect(0, 0, 24, 19); 0089 p.end(); 0090 drag->setPixmap(colorpix); 0091 drag->setHotSpot(QPoint(-5, -7)); 0092 return drag; 0093 } 0094 ///////////////////////////////////////////////////////////////////// 0095 0096 KColorButtonPrivate::KColorButtonPrivate(KColorButton *qq) 0097 : q(qq) 0098 { 0099 m_bdefaultColor = false; 0100 m_alphaChannel = false; 0101 q->setAcceptDrops(true); 0102 0103 QObject::connect(q, &KColorButton::clicked, q, [this]() { 0104 chooseColor(); 0105 }); 0106 } 0107 0108 KColorButton::KColorButton(QWidget *parent) 0109 : QPushButton(parent) 0110 , d(new KColorButtonPrivate(this)) 0111 { 0112 } 0113 0114 KColorButton::KColorButton(const QColor &c, QWidget *parent) 0115 : QPushButton(parent) 0116 , d(new KColorButtonPrivate(this)) 0117 { 0118 d->col = c; 0119 } 0120 0121 KColorButton::KColorButton(const QColor &c, const QColor &defaultColor, QWidget *parent) 0122 : QPushButton(parent) 0123 , d(new KColorButtonPrivate(this)) 0124 { 0125 d->col = c; 0126 setDefaultColor(defaultColor); 0127 } 0128 0129 KColorButton::~KColorButton() = default; 0130 0131 QColor KColorButton::color() const 0132 { 0133 return d->col; 0134 } 0135 0136 void KColorButton::setColor(const QColor &c) 0137 { 0138 if (d->col != c) { 0139 d->col = c; 0140 update(); 0141 Q_EMIT changed(d->col); 0142 } 0143 } 0144 0145 void KColorButton::setAlphaChannelEnabled(bool alpha) 0146 { 0147 d->m_alphaChannel = alpha; 0148 } 0149 0150 bool KColorButton::isAlphaChannelEnabled() const 0151 { 0152 return d->m_alphaChannel; 0153 } 0154 0155 QColor KColorButton::defaultColor() const 0156 { 0157 return d->m_defaultColor; 0158 } 0159 0160 void KColorButton::setDefaultColor(const QColor &c) 0161 { 0162 d->m_bdefaultColor = c.isValid(); 0163 d->m_defaultColor = c; 0164 } 0165 0166 void KColorButtonPrivate::initStyleOption(QStyleOptionButton *opt) const 0167 { 0168 opt->initFrom(q); 0169 opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; 0170 opt->features = QStyleOptionButton::None; 0171 if (q->isDefault()) { 0172 opt->features |= QStyleOptionButton::DefaultButton; 0173 } 0174 opt->text.clear(); 0175 opt->icon = QIcon(); 0176 } 0177 0178 void KColorButton::paintEvent(QPaintEvent *) 0179 { 0180 QPainter painter(this); 0181 QStyle *style = QWidget::style(); 0182 0183 // First, we need to draw the bevel. 0184 QStyleOptionButton butOpt; 0185 d->initStyleOption(&butOpt); 0186 style->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this); 0187 0188 // OK, now we can muck around with drawing out pretty little color box 0189 // First, sort out where it goes 0190 QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this); 0191 int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &butOpt, this) / 2; 0192 labelRect.adjust(shift, shift, -shift, -shift); 0193 int x; 0194 int y; 0195 int w; 0196 int h; 0197 labelRect.getRect(&x, &y, &w, &h); 0198 0199 if (isChecked() || isDown()) { 0200 x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &butOpt, this); 0201 y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &butOpt, this); 0202 } 0203 0204 QColor fillCol = isEnabled() ? d->col : palette().color(backgroundRole()); 0205 qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, nullptr); 0206 if (fillCol.isValid()) { 0207 const QRect rect(x + 1, y + 1, w - 2, h - 2); 0208 if (fillCol.alpha() < 255) { 0209 QPixmap chessboardPattern(16, 16); 0210 QPainter patternPainter(&chessboardPattern); 0211 patternPainter.fillRect(0, 0, 8, 8, Qt::black); 0212 patternPainter.fillRect(8, 8, 8, 8, Qt::black); 0213 patternPainter.fillRect(0, 8, 8, 8, Qt::white); 0214 patternPainter.fillRect(8, 0, 8, 8, Qt::white); 0215 patternPainter.end(); 0216 painter.fillRect(rect, QBrush(chessboardPattern)); 0217 } 0218 painter.fillRect(rect, fillCol); 0219 } 0220 0221 if (hasFocus()) { 0222 QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this); 0223 QStyleOptionFocusRect focusOpt; 0224 focusOpt.initFrom(this); 0225 focusOpt.rect = focusRect; 0226 focusOpt.backgroundColor = palette().window().color(); 0227 style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this); 0228 } 0229 } 0230 0231 QSize KColorButton::sizeHint() const 0232 { 0233 QStyleOptionButton opt; 0234 d->initStyleOption(&opt); 0235 return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this); 0236 } 0237 0238 QSize KColorButton::minimumSizeHint() const 0239 { 0240 QStyleOptionButton opt; 0241 d->initStyleOption(&opt); 0242 return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this); 0243 } 0244 0245 void KColorButton::dragEnterEvent(QDragEnterEvent *event) 0246 { 0247 event->setAccepted(canDecode(event->mimeData()) && isEnabled()); 0248 } 0249 0250 void KColorButton::dropEvent(QDropEvent *event) 0251 { 0252 QColor c = fromMimeData(event->mimeData()); 0253 if (c.isValid()) { 0254 setColor(c); 0255 } 0256 } 0257 0258 void KColorButton::keyPressEvent(QKeyEvent *e) 0259 { 0260 int key = e->key() | e->modifiers(); 0261 0262 if (QKeySequence::keyBindings(QKeySequence::Copy).contains(key)) { 0263 QMimeData *mime = new QMimeData; 0264 populateMimeData(mime, color()); 0265 QApplication::clipboard()->setMimeData(mime, QClipboard::Clipboard); 0266 } else if (QKeySequence::keyBindings(QKeySequence::Paste).contains(key)) { 0267 QColor color = fromMimeData(QApplication::clipboard()->mimeData(QClipboard::Clipboard)); 0268 setColor(color); 0269 } else { 0270 QPushButton::keyPressEvent(e); 0271 } 0272 } 0273 0274 void KColorButton::mousePressEvent(QMouseEvent *e) 0275 { 0276 d->mPos = e->pos(); 0277 QPushButton::mousePressEvent(e); 0278 } 0279 0280 void KColorButton::mouseMoveEvent(QMouseEvent *e) 0281 { 0282 if ((e->buttons() & Qt::LeftButton) && 0283 (e->pos() - d->mPos).manhattanLength() > QApplication::startDragDistance()) { 0284 createDrag(color(), this)->exec(); 0285 setDown(false); 0286 } 0287 } 0288 0289 void KColorButtonPrivate::chooseColor() 0290 { 0291 QColorDialog *dialog = dialogPtr.data(); 0292 if (dialog) { 0293 dialog->show(); 0294 dialog->raise(); 0295 dialog->activateWindow(); 0296 return; 0297 } 0298 0299 dialog = new QColorDialog(q); 0300 dialog->setCurrentColor(q->color()); 0301 dialog->setOption(QColorDialog::ShowAlphaChannel, m_alphaChannel); 0302 dialog->setAttribute(Qt::WA_DeleteOnClose); 0303 QObject::connect(dialog, &QDialog::accepted, q, [this]() { 0304 colorChosen(); 0305 }); 0306 dialogPtr = dialog; 0307 dialog->show(); 0308 } 0309 0310 void KColorButtonPrivate::colorChosen() 0311 { 0312 QColorDialog *dialog = dialogPtr.data(); 0313 if (!dialog) { 0314 return; 0315 } 0316 0317 if (dialog->selectedColor().isValid()) { 0318 q->setColor(dialog->selectedColor()); 0319 } else if (m_bdefaultColor) { 0320 q->setColor(m_defaultColor); 0321 } 0322 } 0323 0324 #include "moc_kcolorbutton.cpp"