1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
4 SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
5 SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "kcolorcombo.h"
11
12#include <QAbstractItemDelegate>
13#include <QApplication>
14#include <QColorDialog>
15#include <QStylePainter>
16
17class KColorComboDelegate : public QAbstractItemDelegate
18{
19 Q_OBJECT
20public:
21 enum ItemRoles {
22 ColorRole = Qt::UserRole + 1,
23 };
24
25 enum LayoutMetrics {
26 FrameMargin = 3,
27 };
28
29 KColorComboDelegate(QObject *parent = nullptr);
30 ~KColorComboDelegate() override;
31
32 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
33 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
34};
35
36static QBrush k_colorcombodelegate_brush(const QModelIndex &index, int role)
37{
38 QBrush brush;
39 QVariant v = index.data(arole: role);
40 if (v.userType() == QMetaType::QBrush) {
41 brush = v.value<QBrush>();
42 } else if (v.userType() == QMetaType::QColor) {
43 brush = QBrush(v.value<QColor>());
44 }
45 return brush;
46}
47
48KColorComboDelegate::KColorComboDelegate(QObject *parent)
49 : QAbstractItemDelegate(parent)
50{
51}
52
53KColorComboDelegate::~KColorComboDelegate()
54{
55}
56
57void KColorComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
58{
59 // background
60 QColor innercolor(Qt::white);
61 bool isSelected = (option.state & QStyle::State_Selected);
62 bool paletteBrush = (k_colorcombodelegate_brush(index, role: Qt::BackgroundRole).style() == Qt::NoBrush);
63 if (isSelected) {
64 innercolor = option.palette.color(cr: QPalette::Highlight);
65 } else {
66 innercolor = option.palette.color(cr: QPalette::Base);
67 }
68 // highlight selected item
69 QStyleOptionViewItem opt(option);
70 opt.showDecorationSelected = true;
71 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
72 style->drawPrimitive(pe: QStyle::PE_PanelItemViewItem, opt: &opt, p: painter, w: opt.widget);
73 QRect innerrect = option.rect.adjusted(xp1: FrameMargin, yp1: FrameMargin, xp2: -FrameMargin, yp2: -FrameMargin);
74 // inner color
75 QVariant cv = index.data(arole: ColorRole);
76 if (cv.userType() == QMetaType::QColor) {
77 QColor tmpcolor = cv.value<QColor>();
78 if (tmpcolor.isValid()) {
79 innercolor = tmpcolor;
80 paletteBrush = false;
81 painter->setPen(Qt::transparent);
82 painter->setBrush(innercolor);
83 QPainter::RenderHints tmpHint = painter->renderHints();
84 painter->setRenderHint(hint: QPainter::Antialiasing);
85 painter->drawRoundedRect(rect: innerrect, xRadius: 2, yRadius: 2);
86 painter->setRenderHints(hints: tmpHint);
87 painter->setBrush(Qt::NoBrush);
88 }
89 }
90 // text
91 QVariant tv = index.data(arole: Qt::DisplayRole);
92 if (tv.userType() == QMetaType::QString) {
93 QString text = tv.toString();
94 QColor textColor;
95 if (paletteBrush) {
96 if (isSelected) {
97 textColor = option.palette.color(cr: QPalette::HighlightedText);
98 } else {
99 textColor = option.palette.color(cr: QPalette::Text);
100 }
101 } else {
102 int unused;
103 int v;
104 innercolor.getHsv(h: &unused, s: &unused, v: &v);
105 if (v > 128) {
106 textColor = Qt::black;
107 } else {
108 textColor = Qt::white;
109 }
110 }
111 painter->setPen(textColor);
112 painter->drawText(r: innerrect.adjusted(xp1: 1, yp1: 1, xp2: -1, yp2: -1), text);
113 }
114}
115
116QSize KColorComboDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
117{
118 Q_UNUSED(index)
119
120 // the width does not matter, as the view will always use the maximum width available
121 return QSize(100, option.fontMetrics.height() + 2 * FrameMargin);
122}
123
124static const uchar standardPalette[][4] = {
125 {255, 255, 255}, // white
126 {192, 192, 192}, // light gray
127 {160, 160, 160}, // gray
128 {128, 128, 128}, // dark gray
129 {0, 0, 0}, // black
130
131 {255, 128, 128}, // light red
132 {255, 192, 128}, // light orange
133 {255, 255, 128}, // light yellow
134 {128, 255, 128}, // light green
135 {128, 255, 255}, // cyan blue
136 {128, 128, 255}, // light blue
137 {255, 128, 255}, // light violet
138 {255, 0, 0}, // red
139 {255, 128, 0}, // orange
140 {255, 255, 0}, // yellow
141 {0, 255, 0}, // green
142 {0, 255, 255}, // light blue
143 {0, 0, 255}, // blue
144 {255, 0, 255}, // violet
145 {128, 0, 0}, // dark red
146 {128, 64, 0}, // dark orange
147 {128, 128, 0}, // dark yellow
148 {0, 128, 0}, // dark green
149 {0, 128, 128}, // dark light blue
150 {0, 0, 128}, // dark blue
151 {128, 0, 128} // dark violet
152};
153
154#define STANDARD_PALETTE_SIZE (int(sizeof(standardPalette) / sizeof(*standardPalette)))
155
156static inline QColor standardColor(int i)
157{
158 const uchar *entry = standardPalette[i];
159 return QColor(entry[0], entry[1], entry[2]);
160}
161
162class KColorComboPrivate
163{
164public:
165 KColorComboPrivate(KColorCombo *qq);
166
167 void addColors();
168 void setCustomColor(const QColor &color, bool lookupInPresets = true);
169
170 // slots
171 void slotActivated(int index);
172 void slotHighlighted(int index);
173
174 KColorCombo *q;
175 QList<QColor> colorList;
176 QColor customColor;
177 QColor internalcolor;
178};
179
180KColorComboPrivate::KColorComboPrivate(KColorCombo *qq)
181 : q(qq)
182 , customColor(Qt::white)
183{
184}
185
186void KColorComboPrivate::setCustomColor(const QColor &color, bool lookupInPresets)
187{
188 if (lookupInPresets) {
189 if (colorList.isEmpty()) {
190 for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
191 if (standardColor(i) == color) {
192 q->setCurrentIndex(i + 1);
193 internalcolor = color;
194 return;
195 }
196 }
197 } else {
198 int i = colorList.indexOf(t: color);
199 if (i >= 0) {
200 q->setCurrentIndex(i + 1);
201 internalcolor = color;
202 return;
203 }
204 }
205 }
206
207 internalcolor = color;
208 customColor = color;
209 q->setItemData(index: 0, value: customColor, role: KColorComboDelegate::ColorRole);
210}
211
212KColorCombo::KColorCombo(QWidget *parent)
213 : QComboBox(parent)
214 , d(new KColorComboPrivate(this))
215{
216 setItemDelegate(new KColorComboDelegate(this));
217 d->addColors();
218
219 connect(sender: this, signal: &QComboBox::activated, context: this, slot: [this](int index) {
220 d->slotActivated(index);
221 });
222 connect(sender: this, signal: &QComboBox::highlighted, context: this, slot: [this](int index) {
223 d->slotHighlighted(index);
224 });
225
226 // select the white color
227 setCurrentIndex(1);
228 d->slotActivated(index: 1);
229
230 setMaxVisibleItems(13);
231}
232
233KColorCombo::~KColorCombo() = default;
234
235void KColorCombo::setColors(const QList<QColor> &colors)
236{
237 clear();
238 d->colorList = colors;
239 d->addColors();
240}
241
242QList<QColor> KColorCombo::colors() const
243{
244 if (d->colorList.isEmpty()) {
245 QList<QColor> list;
246 list.reserve(STANDARD_PALETTE_SIZE);
247 for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
248 list += standardColor(i);
249 }
250 return list;
251 } else {
252 return d->colorList;
253 }
254}
255
256void KColorCombo::setColor(const QColor &col)
257{
258 if (!col.isValid()) {
259 return;
260 }
261
262 if (count() == 0) {
263 d->addColors();
264 }
265
266 d->setCustomColor(color: col, lookupInPresets: true);
267}
268
269QColor KColorCombo::color() const
270{
271 return d->internalcolor;
272}
273
274bool KColorCombo::isCustomColor() const
275{
276 return d->internalcolor == d->customColor;
277}
278
279void KColorCombo::paintEvent(QPaintEvent *event)
280{
281 Q_UNUSED(event)
282 QStylePainter painter(this);
283 painter.setPen(palette().color(cr: QPalette::Text));
284
285 QStyleOptionComboBox opt;
286 initStyleOption(option: &opt);
287 painter.drawComplexControl(cc: QStyle::CC_ComboBox, opt);
288
289 QRect frame = style()->subControlRect(cc: QStyle::CC_ComboBox, opt: &opt, sc: QStyle::SC_ComboBoxEditField, widget: this);
290 painter.setRenderHint(hint: QPainter::Antialiasing);
291 painter.setPen(Qt::transparent);
292 painter.setBrush(QBrush(d->internalcolor));
293 painter.drawRoundedRect(rect: frame.adjusted(xp1: 1, yp1: 1, xp2: -1, yp2: -1), xRadius: 2, yRadius: 2);
294}
295
296void KColorCombo::showEmptyList()
297{
298 clear();
299}
300
301void KColorComboPrivate::slotActivated(int index)
302{
303 if (index == 0) {
304 QColor c = QColorDialog::getColor(initial: customColor, parent: q);
305 if (c.isValid()) {
306 customColor = c;
307 setCustomColor(color: customColor, lookupInPresets: false);
308 }
309 } else if (colorList.isEmpty()) {
310 internalcolor = standardColor(i: index - 1);
311 } else {
312 internalcolor = colorList[index - 1];
313 }
314
315 Q_EMIT q->activated(col: internalcolor);
316}
317
318void KColorComboPrivate::slotHighlighted(int index)
319{
320 if (index == 0) {
321 internalcolor = customColor;
322 } else if (colorList.isEmpty()) {
323 internalcolor = standardColor(i: index - 1);
324 } else {
325 internalcolor = colorList[index - 1];
326 }
327
328 Q_EMIT q->highlighted(col: internalcolor);
329}
330
331void KColorComboPrivate::addColors()
332{
333 q->addItem(atext: KColorCombo::tr(s: "Custom...", c: "@item:inlistbox Custom color"));
334
335 if (colorList.isEmpty()) {
336 for (int i = 0; i < STANDARD_PALETTE_SIZE; ++i) {
337 q->addItem(atext: QString());
338 q->setItemData(index: i + 1, value: standardColor(i), role: KColorComboDelegate::ColorRole);
339 }
340 } else {
341 for (int i = 0, count = colorList.count(); i < count; ++i) {
342 q->addItem(atext: QString());
343 q->setItemData(index: i + 1, value: colorList[i], role: KColorComboDelegate::ColorRole);
344 }
345 }
346}
347
348#include "kcolorcombo.moc"
349#include "moc_kcolorcombo.cpp"
350

source code of kwidgetsaddons/src/kcolorcombo.cpp