1 /*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "kselector.h"
9
10 #include <QPaintEvent>
11 #include <QPainter>
12 #include <QPixmap>
13 #include <QStyle>
14 #include <QStyleOption>
15
16 //-----------------------------------------------------------------------------
17 /*
18 * 1D value selector with contents drawn by derived class.
19 * See KColorDialog for example.
20 */
21
22 #define ARROWSIZE 5
23
24 class KSelectorPrivate
25 {
26 public:
27 bool m_indent = true;
28 QStyle::PrimitiveElement arrowPE = QStyle::PE_IndicatorArrowLeft;
29 };
30
31 class KGradientSelectorPrivate
32 {
33 public:
KGradientSelectorPrivate(KGradientSelector * qq)34 KGradientSelectorPrivate(KGradientSelector *qq)
35 : q(qq)
36 {
37 }
38
39 KGradientSelector *q;
40 QLinearGradient gradient;
41 QString text1;
42 QString text2;
43 };
44
KSelector(QWidget * parent)45 KSelector::KSelector(QWidget *parent)
46 : QAbstractSlider(parent)
47 , d(new KSelectorPrivate)
48 {
49 setOrientation(Qt::Horizontal);
50 }
51
KSelector(Qt::Orientation o,QWidget * parent)52 KSelector::KSelector(Qt::Orientation o, QWidget *parent)
53 : QAbstractSlider(parent)
54 , d(new KSelectorPrivate)
55 {
56 setOrientation(o);
57 if (o == Qt::Horizontal) {
58 setArrowDirection(Qt::UpArrow);
59 }
60 }
61
62 KSelector::~KSelector() = default;
63
setIndent(bool i)64 void KSelector::setIndent(bool i)
65 {
66 d->m_indent = i;
67 }
68
indent() const69 bool KSelector::indent() const
70 {
71 return d->m_indent;
72 }
73
contentsRect() const74 QRect KSelector::contentsRect() const
75 {
76 int w = indent() ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0;
77 // TODO: is the height:width ratio of an indicator arrow always 2:1? hm.
78 int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
79
80 if (orientation() == Qt::Vertical) {
81 if (arrowDirection() == Qt::RightArrow) {
82 return QRect(w + ARROWSIZE, //
83 iw,
84 width() - w * 2 - ARROWSIZE,
85 height() - iw * 2);
86 } else {
87 return QRect(w, //
88 iw,
89 width() - w * 2 - ARROWSIZE,
90 height() - iw * 2);
91 }
92 } else { // Qt::Horizontal
93 if (arrowDirection() == Qt::UpArrow) {
94 return QRect(iw, //
95 w,
96 width() - 2 * iw,
97 height() - w * 2 - ARROWSIZE);
98 } else {
99 return QRect(iw, //
100 w + ARROWSIZE,
101 width() - 2 * iw,
102 height() - w * 2 - ARROWSIZE);
103 }
104 }
105 }
106
paintEvent(QPaintEvent *)107 void KSelector::paintEvent(QPaintEvent *)
108 {
109 QPainter painter;
110 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
111 int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
112
113 painter.begin(this);
114
115 drawContents(&painter);
116
117 QPoint pos = calcArrowPos(value());
118 drawArrow(&painter, pos);
119
120 if (indent()) {
121 QStyleOptionFrame opt;
122 opt.initFrom(this);
123 opt.state = QStyle::State_Sunken;
124 if (orientation() == Qt::Vertical) {
125 opt.rect.adjust(0, iw - w, -5, w - iw);
126 } else {
127 opt.rect.adjust(iw - w, 0, w - iw, -5);
128 }
129 QBrush oldBrush = painter.brush();
130 painter.setBrush(Qt::NoBrush);
131 style()->drawPrimitive(QStyle::PE_Frame, &opt, &painter, this);
132 painter.setBrush(oldBrush);
133 }
134
135 painter.end();
136 }
137
mousePressEvent(QMouseEvent * e)138 void KSelector::mousePressEvent(QMouseEvent *e)
139 {
140 setSliderDown(true);
141 moveArrow(e->pos());
142 }
143
mouseMoveEvent(QMouseEvent * e)144 void KSelector::mouseMoveEvent(QMouseEvent *e)
145 {
146 moveArrow(e->pos());
147 }
148
mouseReleaseEvent(QMouseEvent * e)149 void KSelector::mouseReleaseEvent(QMouseEvent *e)
150 {
151 moveArrow(e->pos());
152 setSliderDown(false);
153 }
154
wheelEvent(QWheelEvent * e)155 void KSelector::wheelEvent(QWheelEvent *e)
156 {
157 int val = value() + e->angleDelta().y() / 120;
158 setSliderDown(true);
159 setValue(val);
160 setSliderDown(false);
161 }
162
moveArrow(const QPoint & pos)163 void KSelector::moveArrow(const QPoint &pos)
164 {
165 int val;
166 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
167 int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
168
169 if (orientation() == Qt::Vertical) {
170 val = (maximum() - minimum()) * (height() - pos.y() - iw) / (height() - iw * 2) + minimum();
171 } else {
172 val = (maximum() - minimum()) * (pos.x() - iw) / (width() - iw * 2) + minimum();
173 }
174
175 setValue(val);
176 update();
177 }
178
calcArrowPos(int val)179 QPoint KSelector::calcArrowPos(int val)
180 {
181 QPoint p;
182 int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
183 int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
184
185 if (orientation() == Qt::Vertical) {
186 p.setY(height() - iw - 1 - (height() - 2 * iw - 1) * val / (maximum() - minimum()));
187
188 if (d->arrowPE == QStyle::PE_IndicatorArrowRight) {
189 p.setX(0);
190 } else {
191 p.setX(width() - 5);
192 }
193 } else {
194 p.setX(iw + (width() - 2 * iw - 1) * val / (maximum() - minimum()));
195
196 if (d->arrowPE == QStyle::PE_IndicatorArrowDown) {
197 p.setY(0);
198 } else {
199 p.setY(height() - 5);
200 }
201 }
202
203 return p;
204 }
205
setArrowDirection(Qt::ArrowType direction)206 void KSelector::setArrowDirection(Qt::ArrowType direction)
207 {
208 switch (direction) {
209 case Qt::UpArrow:
210 if (orientation() == Qt::Horizontal) {
211 d->arrowPE = QStyle::PE_IndicatorArrowUp;
212 } else {
213 d->arrowPE = QStyle::PE_IndicatorArrowLeft;
214 }
215 break;
216 case Qt::DownArrow:
217 if (orientation() == Qt::Horizontal) {
218 d->arrowPE = QStyle::PE_IndicatorArrowDown;
219 } else {
220 d->arrowPE = QStyle::PE_IndicatorArrowRight;
221 }
222 break;
223 case Qt::LeftArrow:
224 if (orientation() == Qt::Vertical) {
225 d->arrowPE = QStyle::PE_IndicatorArrowLeft;
226 } else {
227 d->arrowPE = QStyle::PE_IndicatorArrowDown;
228 }
229 break;
230 case Qt::RightArrow:
231 if (orientation() == Qt::Vertical) {
232 d->arrowPE = QStyle::PE_IndicatorArrowRight;
233 } else {
234 d->arrowPE = QStyle::PE_IndicatorArrowUp;
235 }
236 break;
237
238 case Qt::NoArrow:
239 break;
240 }
241 }
242
arrowDirection() const243 Qt::ArrowType KSelector::arrowDirection() const
244 {
245 switch (d->arrowPE) {
246 case QStyle::PE_IndicatorArrowUp:
247 return Qt::UpArrow;
248 case QStyle::PE_IndicatorArrowDown:
249 return Qt::DownArrow;
250 case QStyle::PE_IndicatorArrowRight:
251 return Qt::RightArrow;
252 case QStyle::PE_IndicatorArrowLeft:
253 default:
254 return Qt::LeftArrow;
255 }
256 }
257
drawContents(QPainter *)258 void KSelector::drawContents(QPainter *)
259 {
260 }
261
drawArrow(QPainter * painter,const QPoint & pos)262 void KSelector::drawArrow(QPainter *painter, const QPoint &pos)
263 {
264 painter->setPen(QPen());
265 painter->setBrush(QBrush(palette().color(QPalette::ButtonText)));
266
267 QStyleOption o;
268
269 if (orientation() == Qt::Vertical) {
270 o.rect = QRect(pos.x(), pos.y() - ARROWSIZE / 2, ARROWSIZE, ARROWSIZE);
271 } else {
272 o.rect = QRect(pos.x() - ARROWSIZE / 2, pos.y(), ARROWSIZE, ARROWSIZE);
273 }
274 style()->drawPrimitive(d->arrowPE, &o, painter, this);
275 }
276
277 //----------------------------------------------------------------------------
278
KGradientSelector(QWidget * parent)279 KGradientSelector::KGradientSelector(QWidget *parent)
280 : KSelector(parent)
281 , d(new KGradientSelectorPrivate(this))
282 {
283 }
284
KGradientSelector(Qt::Orientation o,QWidget * parent)285 KGradientSelector::KGradientSelector(Qt::Orientation o, QWidget *parent)
286 : KSelector(o, parent)
287 , d(new KGradientSelectorPrivate(this))
288 {
289 }
290
291 KGradientSelector::~KGradientSelector() = default;
292
drawContents(QPainter * painter)293 void KGradientSelector::drawContents(QPainter *painter)
294 {
295 d->gradient.setStart(contentsRect().topLeft());
296 if (orientation() == Qt::Vertical) {
297 d->gradient.setFinalStop(contentsRect().bottomLeft());
298 } else {
299 d->gradient.setFinalStop(contentsRect().topRight());
300 }
301 QBrush gradientBrush(d->gradient);
302
303 if (!gradientBrush.isOpaque()) {
304 QPixmap chessboardPattern(16, 16);
305 QPainter patternPainter(&chessboardPattern);
306 patternPainter.fillRect(0, 0, 8, 8, Qt::black);
307 patternPainter.fillRect(8, 8, 8, 8, Qt::black);
308 patternPainter.fillRect(0, 8, 8, 8, Qt::white);
309 patternPainter.fillRect(8, 0, 8, 8, Qt::white);
310 patternPainter.end();
311 painter->fillRect(contentsRect(), QBrush(chessboardPattern));
312 }
313 painter->fillRect(contentsRect(), gradientBrush);
314
315 if (orientation() == Qt::Vertical) {
316 int yPos = contentsRect().top() + painter->fontMetrics().ascent() + 2;
317 int xPos = contentsRect().left() + (contentsRect().width() - painter->fontMetrics().horizontalAdvance(d->text2)) / 2;
318 QPen pen(qGray(firstColor().rgb()) > 180 ? Qt::black : Qt::white);
319 painter->setPen(pen);
320 painter->drawText(xPos, yPos, d->text2);
321
322 yPos = contentsRect().bottom() - painter->fontMetrics().descent() - 2;
323 xPos = contentsRect().left() + (contentsRect().width() - painter->fontMetrics().horizontalAdvance(d->text1)) / 2;
324 pen.setColor(qGray(secondColor().rgb()) > 180 ? Qt::black : Qt::white);
325 painter->setPen(pen);
326 painter->drawText(xPos, yPos, d->text1);
327 } else {
328 int yPos = contentsRect().bottom() - painter->fontMetrics().descent() - 2;
329
330 QPen pen(qGray(firstColor().rgb()) > 180 ? Qt::black : Qt::white);
331 painter->setPen(pen);
332 painter->drawText(contentsRect().left() + 2, yPos, d->text1);
333
334 pen.setColor(qGray(secondColor().rgb()) > 180 ? Qt::black : Qt::white);
335 painter->setPen(pen);
336 painter->drawText(contentsRect().right() - painter->fontMetrics().horizontalAdvance(d->text2) - 2, yPos, d->text2);
337 }
338 }
339
minimumSize() const340 QSize KGradientSelector::minimumSize() const
341 {
342 return sizeHint();
343 }
344
setStops(const QGradientStops & stops)345 void KGradientSelector::setStops(const QGradientStops &stops)
346 {
347 d->gradient.setStops(stops);
348 update();
349 }
350
stops() const351 QGradientStops KGradientSelector::stops() const
352 {
353 return d->gradient.stops();
354 }
355
setColors(const QColor & col1,const QColor & col2)356 void KGradientSelector::setColors(const QColor &col1, const QColor &col2)
357 {
358 d->gradient.setColorAt(0.0, col1);
359 d->gradient.setColorAt(1.0, col2);
360 update();
361 }
362
setText(const QString & t1,const QString & t2)363 void KGradientSelector::setText(const QString &t1, const QString &t2)
364 {
365 d->text1 = t1;
366 d->text2 = t2;
367 update();
368 }
369
setFirstColor(const QColor & col)370 void KGradientSelector::setFirstColor(const QColor &col)
371 {
372 d->gradient.setColorAt(0.0, col);
373 update();
374 }
375
setSecondColor(const QColor & col)376 void KGradientSelector::setSecondColor(const QColor &col)
377 {
378 d->gradient.setColorAt(1.0, col);
379 update();
380 }
381
setFirstText(const QString & t)382 void KGradientSelector::setFirstText(const QString &t)
383 {
384 d->text1 = t;
385 update();
386 }
387
setSecondText(const QString & t)388 void KGradientSelector::setSecondText(const QString &t)
389 {
390 d->text2 = t;
391 update();
392 }
393
firstColor() const394 QColor KGradientSelector::firstColor() const
395 {
396 return d->gradient.stops().first().second;
397 }
398
secondColor() const399 QColor KGradientSelector::secondColor() const
400 {
401 return d->gradient.stops().last().second;
402 }
403
firstText() const404 QString KGradientSelector::firstText() const
405 {
406 return d->text1;
407 }
408
secondText() const409 QString KGradientSelector::secondText() const
410 {
411 return d->text2;
412 }
413