File indexing completed on 2024-04-28 03:59:02

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
0004     SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
0005     SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kcolorcombo.h"
0011 
0012 #include <QAbstractItemDelegate>
0013 #include <QApplication>
0014 #include <QColorDialog>
0015 #include <QStylePainter>
0016 
0017 class KColorComboDelegate : public QAbstractItemDelegate
0018 {
0019     Q_OBJECT
0020 public:
0021     enum ItemRoles {
0022         ColorRole = Qt::UserRole + 1,
0023     };
0024 
0025     enum LayoutMetrics {
0026         FrameMargin = 3,
0027     };
0028 
0029     KColorComboDelegate(QObject *parent = nullptr);
0030     ~KColorComboDelegate() override;
0031 
0032     void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
0033     QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
0034 };
0035 
0036 static QBrush k_colorcombodelegate_brush(const QModelIndex &index, int role)
0037 {
0038     QBrush brush;
0039     QVariant v = index.data(role);
0040     if (v.userType() == QMetaType::QBrush) {
0041         brush = v.value<QBrush>();
0042     } else if (v.userType() == QMetaType::QColor) {
0043         brush = QBrush(v.value<QColor>());
0044     }
0045     return brush;
0046 }
0047 
0048 KColorComboDelegate::KColorComboDelegate(QObject *parent)
0049     : QAbstractItemDelegate(parent)
0050 {
0051 }
0052 
0053 KColorComboDelegate::~KColorComboDelegate()
0054 {
0055 }
0056 
0057 void KColorComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0058 {
0059     // background
0060     QColor innercolor(Qt::white);
0061     bool isSelected = (option.state & QStyle::State_Selected);
0062     bool paletteBrush = (k_colorcombodelegate_brush(index, Qt::BackgroundRole).style() == Qt::NoBrush);
0063     if (isSelected) {
0064         innercolor = option.palette.color(QPalette::Highlight);
0065     } else {
0066         innercolor = option.palette.color(QPalette::Base);
0067     }
0068     // highlight selected item
0069     QStyleOptionViewItem opt(option);
0070     opt.showDecorationSelected = true;
0071     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0072     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
0073     QRect innerrect = option.rect.adjusted(FrameMargin, FrameMargin, -FrameMargin, -FrameMargin);
0074     // inner color
0075     QVariant cv = index.data(ColorRole);
0076     if (cv.userType() == QMetaType::QColor) {
0077         QColor tmpcolor = cv.value<QColor>();
0078         if (tmpcolor.isValid()) {
0079             innercolor = tmpcolor;
0080             paletteBrush = false;
0081             painter->setPen(Qt::transparent);
0082             painter->setBrush(innercolor);
0083             QPainter::RenderHints tmpHint = painter->renderHints();
0084             painter->setRenderHint(QPainter::Antialiasing);
0085             painter->drawRoundedRect(innerrect, 2, 2);
0086             painter->setRenderHints(tmpHint);
0087             painter->setBrush(Qt::NoBrush);
0088         }
0089     }
0090     // text
0091     QVariant tv = index.data(Qt::DisplayRole);
0092     if (tv.userType() == QMetaType::QString) {
0093         QString text = tv.toString();
0094         QColor textColor;
0095         if (paletteBrush) {
0096             if (isSelected) {
0097                 textColor = option.palette.color(QPalette::HighlightedText);
0098             } else {
0099                 textColor = option.palette.color(QPalette::Text);
0100             }
0101         } else {
0102             int unused;
0103             int v;
0104             innercolor.getHsv(&unused, &unused, &v);
0105             if (v > 128) {
0106                 textColor = Qt::black;
0107             } else {
0108                 textColor = Qt::white;
0109             }
0110         }
0111         painter->setPen(textColor);
0112         painter->drawText(innerrect.adjusted(1, 1, -1, -1), text);
0113     }
0114 }
0115 
0116 QSize KColorComboDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0117 {
0118     Q_UNUSED(index)
0119 
0120     // the width does not matter, as the view will always use the maximum width available
0121     return QSize(100, option.fontMetrics.height() + 2 * FrameMargin);
0122 }
0123 
0124 static const uchar standardPalette[][4] = {
0125     {255, 255, 255}, // white
0126     {192, 192, 192}, // light gray
0127     {160, 160, 160}, // gray
0128     {128, 128, 128}, // dark gray
0129     {0, 0, 0}, // black
0130 
0131     {255, 128, 128}, // light red
0132     {255, 192, 128}, // light orange
0133     {255, 255, 128}, // light yellow
0134     {128, 255, 128}, // light green
0135     {128, 255, 255}, // cyan blue
0136     {128, 128, 255}, // light blue
0137     {255, 128, 255}, // light violet
0138     {255, 0, 0}, // red
0139     {255, 128, 0}, // orange
0140     {255, 255, 0}, // yellow
0141     {0, 255, 0}, // green
0142     {0, 255, 255}, // light blue
0143     {0, 0, 255}, // blue
0144     {255, 0, 255}, // violet
0145     {128, 0, 0}, // dark red
0146     {128, 64, 0}, // dark orange
0147     {128, 128, 0}, // dark yellow
0148     {0, 128, 0}, // dark green
0149     {0, 128, 128}, // dark light blue
0150     {0, 0, 128}, // dark blue
0151     {128, 0, 128} // dark violet
0152 };
0153 
0154 #define STANDARD_PALETTE_SIZE (int(sizeof(standardPalette) / sizeof(*standardPalette)))
0155 
0156 static inline QColor standardColor(int i)
0157 {
0158     const uchar *entry = standardPalette[i];
0159     return QColor(entry[0], entry[1], entry[2]);
0160 }
0161 
0162 class KColorComboPrivate
0163 {
0164 public:
0165     KColorComboPrivate(KColorCombo *qq);
0166 
0167     void addColors();
0168     void setCustomColor(const QColor &color, bool lookupInPresets = true);
0169 
0170     // slots
0171     void slotActivated(int index);
0172     void slotHighlighted(int index);
0173 
0174     KColorCombo *q;
0175     QList<QColor> colorList;
0176     QColor customColor;
0177     QColor internalcolor;
0178 };
0179 
0180 KColorComboPrivate::KColorComboPrivate(KColorCombo *qq)
0181     : q(qq)
0182     , customColor(Qt::white)
0183 {
0184 }
0185 
0186 void KColorComboPrivate::setCustomColor(const QColor &color, bool lookupInPresets)
0187 {
0188     if (lookupInPresets) {
0189         if (colorList.isEmpty()) {
0190             for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
0191                 if (standardColor(i) == color) {
0192                     q->setCurrentIndex(i + 1);
0193                     internalcolor = color;
0194                     return;
0195                 }
0196             }
0197         } else {
0198             int i = colorList.indexOf(color);
0199             if (i >= 0) {
0200                 q->setCurrentIndex(i + 1);
0201                 internalcolor = color;
0202                 return;
0203             }
0204         }
0205     }
0206 
0207     internalcolor = color;
0208     customColor = color;
0209     q->setItemData(0, customColor, KColorComboDelegate::ColorRole);
0210 }
0211 
0212 KColorCombo::KColorCombo(QWidget *parent)
0213     : QComboBox(parent)
0214     , d(new KColorComboPrivate(this))
0215 {
0216     setItemDelegate(new KColorComboDelegate(this));
0217     d->addColors();
0218 
0219     connect(this, &QComboBox::activated, this, [this](int index) {
0220         d->slotActivated(index);
0221     });
0222     connect(this, &QComboBox::highlighted, this, [this](int index) {
0223         d->slotHighlighted(index);
0224     });
0225 
0226     // select the white color
0227     setCurrentIndex(1);
0228     d->slotActivated(1);
0229 
0230     setMaxVisibleItems(13);
0231 }
0232 
0233 KColorCombo::~KColorCombo() = default;
0234 
0235 void KColorCombo::setColors(const QList<QColor> &colors)
0236 {
0237     clear();
0238     d->colorList = colors;
0239     d->addColors();
0240 }
0241 
0242 QList<QColor> KColorCombo::colors() const
0243 {
0244     if (d->colorList.isEmpty()) {
0245         QList<QColor> list;
0246         list.reserve(STANDARD_PALETTE_SIZE);
0247         for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
0248             list += standardColor(i);
0249         }
0250         return list;
0251     } else {
0252         return d->colorList;
0253     }
0254 }
0255 
0256 void KColorCombo::setColor(const QColor &col)
0257 {
0258     if (!col.isValid()) {
0259         return;
0260     }
0261 
0262     if (count() == 0) {
0263         d->addColors();
0264     }
0265 
0266     d->setCustomColor(col, true);
0267 }
0268 
0269 QColor KColorCombo::color() const
0270 {
0271     return d->internalcolor;
0272 }
0273 
0274 bool KColorCombo::isCustomColor() const
0275 {
0276     return d->internalcolor == d->customColor;
0277 }
0278 
0279 void KColorCombo::paintEvent(QPaintEvent *event)
0280 {
0281     Q_UNUSED(event)
0282     QStylePainter painter(this);
0283     painter.setPen(palette().color(QPalette::Text));
0284 
0285     QStyleOptionComboBox opt;
0286     initStyleOption(&opt);
0287     painter.drawComplexControl(QStyle::CC_ComboBox, opt);
0288 
0289     QRect frame = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
0290     painter.setRenderHint(QPainter::Antialiasing);
0291     painter.setPen(Qt::transparent);
0292     painter.setBrush(QBrush(d->internalcolor));
0293     painter.drawRoundedRect(frame.adjusted(1, 1, -1, -1), 2, 2);
0294 }
0295 
0296 void KColorCombo::showEmptyList()
0297 {
0298     clear();
0299 }
0300 
0301 void KColorComboPrivate::slotActivated(int index)
0302 {
0303     if (index == 0) {
0304         QColor c = QColorDialog::getColor(customColor, q);
0305         if (c.isValid()) {
0306             customColor = c;
0307             setCustomColor(customColor, false);
0308         }
0309     } else if (colorList.isEmpty()) {
0310         internalcolor = standardColor(index - 1);
0311     } else {
0312         internalcolor = colorList[index - 1];
0313     }
0314 
0315     Q_EMIT q->activated(internalcolor);
0316 }
0317 
0318 void KColorComboPrivate::slotHighlighted(int index)
0319 {
0320     if (index == 0) {
0321         internalcolor = customColor;
0322     } else if (colorList.isEmpty()) {
0323         internalcolor = standardColor(index - 1);
0324     } else {
0325         internalcolor = colorList[index - 1];
0326     }
0327 
0328     Q_EMIT q->highlighted(internalcolor);
0329 }
0330 
0331 void KColorComboPrivate::addColors()
0332 {
0333     q->addItem(KColorCombo::tr("Custom...", "@item:inlistbox Custom color"));
0334 
0335     if (colorList.isEmpty()) {
0336         for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
0337             q->addItem(QString());
0338             q->setItemData(i + 1, standardColor(i), KColorComboDelegate::ColorRole);
0339         }
0340     } else {
0341         for (int i = 0, count = colorList.count(); i < count; ++i) {
0342             q->addItem(QString());
0343             q->setItemData(i + 1, colorList[i], KColorComboDelegate::ColorRole);
0344         }
0345     }
0346 }
0347 
0348 #include "kcolorcombo.moc"
0349 #include "moc_kcolorcombo.cpp"