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: |
34 | KGradientSelectorPrivate(KGradientSelector *qq) |
35 | : q(qq) |
36 | { |
37 | } |
38 | |
39 | KGradientSelector *q; |
40 | QLinearGradient gradient; |
41 | QString text1; |
42 | QString text2; |
43 | }; |
44 | |
45 | KSelector::KSelector(QWidget *parent) |
46 | : QAbstractSlider(parent) |
47 | , d(new KSelectorPrivate) |
48 | { |
49 | setOrientation(Qt::Horizontal); |
50 | } |
51 | |
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 | |
64 | void KSelector::setIndent(bool i) |
65 | { |
66 | d->m_indent = i; |
67 | } |
68 | |
69 | bool KSelector::indent() const |
70 | { |
71 | return d->m_indent; |
72 | } |
73 | |
74 | QRect 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 | |
107 | void 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 | |
138 | void KSelector::mousePressEvent(QMouseEvent *e) |
139 | { |
140 | setSliderDown(true); |
141 | moveArrow(pos: e->pos()); |
142 | } |
143 | |
144 | void KSelector::mouseMoveEvent(QMouseEvent *e) |
145 | { |
146 | moveArrow(pos: e->pos()); |
147 | } |
148 | |
149 | void KSelector::mouseReleaseEvent(QMouseEvent *e) |
150 | { |
151 | moveArrow(pos: e->pos()); |
152 | setSliderDown(false); |
153 | } |
154 | |
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 | |
163 | void 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 | |
179 | QPoint 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 | |
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 | |
243 | 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 | |
258 | void KSelector::drawContents(QPainter *) |
259 | { |
260 | } |
261 | |
262 | void 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 | |
279 | KGradientSelector::KGradientSelector(QWidget *parent) |
280 | : KSelector(parent) |
281 | , d(new KGradientSelectorPrivate(this)) |
282 | { |
283 | } |
284 | |
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 | |
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(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 | |
340 | QSize KGradientSelector::minimumSize() const |
341 | { |
342 | return sizeHint(); |
343 | } |
344 | |
345 | void KGradientSelector::setStops(const QGradientStops &stops) |
346 | { |
347 | d->gradient.setStops(stops); |
348 | update(); |
349 | } |
350 | |
351 | QGradientStops KGradientSelector::stops() const |
352 | { |
353 | return d->gradient.stops(); |
354 | } |
355 | |
356 | void 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 | |
363 | void KGradientSelector::setText(const QString &t1, const QString &t2) |
364 | { |
365 | d->text1 = t1; |
366 | d->text2 = t2; |
367 | update(); |
368 | } |
369 | |
370 | void KGradientSelector::setFirstColor(const QColor &col) |
371 | { |
372 | d->gradient.setColorAt(pos: 0.0, color: col); |
373 | update(); |
374 | } |
375 | |
376 | void KGradientSelector::setSecondColor(const QColor &col) |
377 | { |
378 | d->gradient.setColorAt(pos: 1.0, color: col); |
379 | update(); |
380 | } |
381 | |
382 | void KGradientSelector::setFirstText(const QString &t) |
383 | { |
384 | d->text1 = t; |
385 | update(); |
386 | } |
387 | |
388 | void KGradientSelector::setSecondText(const QString &t) |
389 | { |
390 | d->text2 = t; |
391 | update(); |
392 | } |
393 | |
394 | QColor KGradientSelector::firstColor() const |
395 | { |
396 | return d->gradient.stops().first().second; |
397 | } |
398 | |
399 | QColor KGradientSelector::secondColor() const |
400 | { |
401 | return d->gradient.stops().last().second; |
402 | } |
403 | |
404 | QString KGradientSelector::firstText() const |
405 | { |
406 | return d->text1; |
407 | } |
408 | |
409 | QString KGradientSelector::secondText() const |
410 | { |
411 | return d->text2; |
412 | } |
413 | |
414 | #include "moc_kselector.cpp" |
415 | |