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
24class KSelectorPrivate
25{
26public:
27 bool m_indent = true;
28 QStyle::PrimitiveElement arrowPE = QStyle::PE_IndicatorArrowLeft;
29};
30
31class KGradientSelectorPrivate
32{
33public:
34 KGradientSelectorPrivate(KGradientSelector *qq)
35 : q(qq)
36 {
37 }
38
39 KGradientSelector *q;
40 QLinearGradient gradient;
41 QString text1;
42 QString text2;
43};
44
45KSelector::KSelector(QWidget *parent)
46 : QAbstractSlider(parent)
47 , d(new KSelectorPrivate)
48{
49 setOrientation(Qt::Horizontal);
50}
51
52KSelector::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
62KSelector::~KSelector() = default;
63
64void KSelector::setIndent(bool i)
65{
66 d->m_indent = i;
67}
68
69bool KSelector::indent() const
70{
71 return d->m_indent;
72}
73
74QRect KSelector::contentsRect() const
75{
76 int w = indent() ? style()->pixelMetric(metric: 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
107void KSelector::paintEvent(QPaintEvent *)
108{
109 QPainter painter;
110 int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth);
111 int iw = (w < ARROWSIZE) ? ARROWSIZE : w;
112
113 painter.begin(this);
114
115 if (indent()) {
116 QStyleOptionFrame opt;
117 opt.initFrom(w: this);
118 opt.state = QStyle::State_Sunken;
119 if (orientation() == Qt::Vertical) {
120 opt.rect.adjust(dx1: 0, dy1: iw - w, dx2: -5, dy2: w - iw);
121 } else {
122 opt.rect.adjust(dx1: iw - w, dy1: 0, dx2: w - iw, dy2: -5);
123 }
124 QBrush oldBrush = painter.brush();
125 painter.setBrush(Qt::NoBrush);
126 style()->drawPrimitive(pe: QStyle::PE_Frame, opt: &opt, p: &painter, w: this);
127 painter.setBrush(oldBrush);
128 }
129
130 drawContents(&painter);
131
132 QPoint pos = calcArrowPos(val: value());
133 drawArrow(painter: &painter, pos);
134
135 painter.end();
136}
137
138void KSelector::mousePressEvent(QMouseEvent *e)
139{
140 setSliderDown(true);
141 moveArrow(pos: e->pos());
142}
143
144void KSelector::mouseMoveEvent(QMouseEvent *e)
145{
146 moveArrow(pos: e->pos());
147}
148
149void KSelector::mouseReleaseEvent(QMouseEvent *e)
150{
151 moveArrow(pos: e->pos());
152 setSliderDown(false);
153}
154
155void KSelector::wheelEvent(QWheelEvent *e)
156{
157 int val = value() + e->angleDelta().y() / 120;
158 setSliderDown(true);
159 setValue(val);
160 setSliderDown(false);
161}
162
163void KSelector::moveArrow(const QPoint &pos)
164{
165 int val;
166 int w = style()->pixelMetric(metric: 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
179QPoint KSelector::calcArrowPos(int val)
180{
181 QPoint p;
182 int w = style()->pixelMetric(metric: 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
206void 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
243Qt::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
258void KSelector::drawContents(QPainter *)
259{
260}
261
262void KSelector::drawArrow(QPainter *painter, const QPoint &pos)
263{
264 painter->setPen(QPen());
265 painter->setBrush(QBrush(palette().color(cr: 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(pe: d->arrowPE, opt: &o, p: painter, w: this);
275}
276
277//----------------------------------------------------------------------------
278
279KGradientSelector::KGradientSelector(QWidget *parent)
280 : KSelector(parent)
281 , d(new KGradientSelectorPrivate(this))
282{
283}
284
285KGradientSelector::KGradientSelector(Qt::Orientation o, QWidget *parent)
286 : KSelector(o, parent)
287 , d(new KGradientSelectorPrivate(this))
288{
289}
290
291KGradientSelector::~KGradientSelector() = default;
292
293void 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(x: 0, y: 0, w: 8, h: 8, c: Qt::black);
307 patternPainter.fillRect(x: 8, y: 8, w: 8, h: 8, c: Qt::black);
308 patternPainter.fillRect(x: 0, y: 8, w: 8, h: 8, c: Qt::white);
309 patternPainter.fillRect(x: 8, y: 0, w: 8, h: 8, c: 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(rgb: firstColor().rgb()) > 180 ? Qt::black : Qt::white);
319 painter->setPen(pen);
320 painter->drawText(x: xPos, y: yPos, s: 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(rgb: secondColor().rgb()) > 180 ? Qt::black : Qt::white);
325 painter->setPen(pen);
326 painter->drawText(x: xPos, y: yPos, s: d->text1);
327 } else {
328 int yPos = contentsRect().bottom() - painter->fontMetrics().descent() - 2;
329
330 QPen pen(qGray(rgb: firstColor().rgb()) > 180 ? Qt::black : Qt::white);
331 painter->setPen(pen);
332 painter->drawText(x: contentsRect().left() + 2, y: yPos, s: d->text1);
333
334 pen.setColor(qGray(rgb: secondColor().rgb()) > 180 ? Qt::black : Qt::white);
335 painter->setPen(pen);
336 painter->drawText(x: contentsRect().right() - painter->fontMetrics().horizontalAdvance(d->text2) - 2, y: yPos, s: d->text2);
337 }
338}
339
340QSize KGradientSelector::minimumSize() const
341{
342 return sizeHint();
343}
344
345void KGradientSelector::setStops(const QGradientStops &stops)
346{
347 d->gradient.setStops(stops);
348 update();
349}
350
351QGradientStops KGradientSelector::stops() const
352{
353 return d->gradient.stops();
354}
355
356void KGradientSelector::setColors(const QColor &col1, const QColor &col2)
357{
358 d->gradient.setColorAt(pos: 0.0, color: col1);
359 d->gradient.setColorAt(pos: 1.0, color: col2);
360 update();
361}
362
363void KGradientSelector::setText(const QString &t1, const QString &t2)
364{
365 d->text1 = t1;
366 d->text2 = t2;
367 update();
368}
369
370void KGradientSelector::setFirstColor(const QColor &col)
371{
372 d->gradient.setColorAt(pos: 0.0, color: col);
373 update();
374}
375
376void KGradientSelector::setSecondColor(const QColor &col)
377{
378 d->gradient.setColorAt(pos: 1.0, color: col);
379 update();
380}
381
382void KGradientSelector::setFirstText(const QString &t)
383{
384 d->text1 = t;
385 update();
386}
387
388void KGradientSelector::setSecondText(const QString &t)
389{
390 d->text2 = t;
391 update();
392}
393
394QColor KGradientSelector::firstColor() const
395{
396 return d->gradient.stops().first().second;
397}
398
399QColor KGradientSelector::secondColor() const
400{
401 return d->gradient.stops().last().second;
402}
403
404QString KGradientSelector::firstText() const
405{
406 return d->text1;
407}
408
409QString KGradientSelector::secondText() const
410{
411 return d->text2;
412}
413
414#include "moc_kselector.cpp"
415

source code of kwidgetsaddons/src/kselector.cpp