| 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 "kxyselector.h" |
| 9 | |
| 10 | #include "loggingcategory.h" |
| 11 | |
| 12 | #include <QMouseEvent> |
| 13 | #include <QPainter> |
| 14 | #include <QStyle> |
| 15 | #include <QStyleOptionFrame> |
| 16 | |
| 17 | //----------------------------------------------------------------------------- |
| 18 | /* |
| 19 | * 2D value selector. |
| 20 | * The contents of the selector are drawn by derived class. |
| 21 | */ |
| 22 | |
| 23 | class KXYSelectorPrivate |
| 24 | { |
| 25 | public: |
| 26 | KXYSelectorPrivate(KXYSelector *qq) |
| 27 | : q(qq) |
| 28 | , xPos(0) |
| 29 | , yPos(0) |
| 30 | , minX(0) |
| 31 | , maxX(100) |
| 32 | , minY(0) |
| 33 | , maxY(100) |
| 34 | , m_markerColor(Qt::white) |
| 35 | { |
| 36 | } |
| 37 | |
| 38 | void setValues(int _xPos, int _yPos); |
| 39 | |
| 40 | KXYSelector *const q; |
| 41 | int px; |
| 42 | int py; |
| 43 | int xPos; |
| 44 | int yPos; |
| 45 | int minX; |
| 46 | int maxX; |
| 47 | int minY; |
| 48 | int maxY; |
| 49 | QColor m_markerColor; |
| 50 | }; |
| 51 | |
| 52 | KXYSelector::KXYSelector(QWidget *parent) |
| 53 | : QWidget(parent) |
| 54 | , d(new KXYSelectorPrivate(this)) |
| 55 | { |
| 56 | } |
| 57 | |
| 58 | KXYSelector::~KXYSelector() = default; |
| 59 | |
| 60 | int KXYSelector::xValue() const |
| 61 | { |
| 62 | return d->xPos; |
| 63 | } |
| 64 | |
| 65 | int KXYSelector::yValue() const |
| 66 | { |
| 67 | return d->yPos; |
| 68 | } |
| 69 | |
| 70 | void KXYSelector::setRange(int _minX, int _minY, int _maxX, int _maxY) |
| 71 | { |
| 72 | if (_maxX == _minX) { |
| 73 | qCWarning(KWidgetsAddonsLog) << "KXYSelector::setRange invalid range: " << _maxX << " == " << _minX << " (for X) " ; |
| 74 | return; |
| 75 | } |
| 76 | if (_maxY == _minY) { |
| 77 | qCWarning(KWidgetsAddonsLog) << "KXYSelector::setRange invalid range: " << _maxY << " == " << _minY << " (for Y) " ; |
| 78 | return; |
| 79 | } |
| 80 | |
| 81 | int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 82 | d->px = w; |
| 83 | d->py = w; |
| 84 | d->minX = _minX; |
| 85 | d->minY = _minY; |
| 86 | d->maxX = _maxX; |
| 87 | d->maxY = _maxY; |
| 88 | } |
| 89 | |
| 90 | void KXYSelector::setXValue(int _xPos) |
| 91 | { |
| 92 | setValues(xPos: _xPos, yPos: d->yPos); |
| 93 | } |
| 94 | |
| 95 | void KXYSelector::setYValue(int _yPos) |
| 96 | { |
| 97 | setValues(xPos: d->xPos, yPos: _yPos); |
| 98 | } |
| 99 | |
| 100 | void KXYSelector::setValues(int _xPos, int _yPos) |
| 101 | { |
| 102 | d->setValues(_xPos, _yPos); |
| 103 | } |
| 104 | |
| 105 | void KXYSelectorPrivate::setValues(int _xPos, int _yPos) |
| 106 | { |
| 107 | int w = q->style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 108 | |
| 109 | xPos = _xPos; |
| 110 | yPos = _yPos; |
| 111 | |
| 112 | if (xPos > maxX) { |
| 113 | xPos = maxX; |
| 114 | } else if (xPos < minX) { |
| 115 | xPos = minX; |
| 116 | } |
| 117 | |
| 118 | if (yPos > maxY) { |
| 119 | yPos = maxY; |
| 120 | } else if (yPos < minY) { |
| 121 | yPos = minY; |
| 122 | } |
| 123 | |
| 124 | Q_ASSERT(maxX != minX); |
| 125 | int xp = w + (q->width() - 2 * w) * xPos / (maxX - minX); |
| 126 | |
| 127 | Q_ASSERT(maxY != minY); |
| 128 | int yp = q->height() - w - (q->height() - 2 * w) * yPos / (maxY - minY); |
| 129 | |
| 130 | q->setPosition(xp, yp); |
| 131 | } |
| 132 | |
| 133 | void KXYSelector::setMarkerColor(const QColor &col) |
| 134 | { |
| 135 | d->m_markerColor = col; |
| 136 | } |
| 137 | |
| 138 | QRect KXYSelector::contentsRect() const |
| 139 | { |
| 140 | int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 141 | return rect().adjusted(xp1: w, yp1: w, xp2: -w, yp2: -w); |
| 142 | } |
| 143 | |
| 144 | QSize KXYSelector::minimumSizeHint() const |
| 145 | { |
| 146 | int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 147 | return QSize(2 * w, 2 * w); |
| 148 | } |
| 149 | |
| 150 | void KXYSelector::paintEvent(QPaintEvent * /* ev */) |
| 151 | { |
| 152 | QStyleOptionFrame opt; |
| 153 | opt.initFrom(w: this); |
| 154 | |
| 155 | QPainter painter; |
| 156 | painter.begin(this); |
| 157 | |
| 158 | drawContents(&painter); |
| 159 | drawMarker(p: &painter, xp: d->px, yp: d->py); |
| 160 | |
| 161 | style()->drawPrimitive(pe: QStyle::PE_Frame, opt: &opt, p: &painter, w: this); |
| 162 | |
| 163 | painter.end(); |
| 164 | } |
| 165 | |
| 166 | void KXYSelector::mousePressEvent(QMouseEvent *e) |
| 167 | { |
| 168 | mouseMoveEvent(e); |
| 169 | } |
| 170 | |
| 171 | void KXYSelector::mouseMoveEvent(QMouseEvent *e) |
| 172 | { |
| 173 | int xVal; |
| 174 | int yVal; |
| 175 | int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 176 | valuesFromPosition(x: e->pos().x() - w, y: e->pos().y() - w, xVal, yVal); |
| 177 | setValues(xPos: xVal, yPos: yVal); |
| 178 | |
| 179 | Q_EMIT valueChanged(x: d->xPos, y: d->yPos); |
| 180 | } |
| 181 | |
| 182 | void KXYSelector::wheelEvent(QWheelEvent *e) |
| 183 | { |
| 184 | setValues(xPos: xValue() + e->angleDelta().x() / 120, yPos: yValue() + e->angleDelta().y() / 120); |
| 185 | Q_EMIT valueChanged(x: d->xPos, y: d->yPos); |
| 186 | } |
| 187 | |
| 188 | void KXYSelector::valuesFromPosition(int x, int y, int &xVal, int &yVal) const |
| 189 | { |
| 190 | int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 191 | |
| 192 | xVal = ((d->maxX - d->minX) * (x - w)) / (width() - 2 * w); |
| 193 | yVal = d->maxY - (((d->maxY - d->minY) * (y - w)) / (height() - 2 * w)); |
| 194 | |
| 195 | if (xVal > d->maxX) { |
| 196 | xVal = d->maxX; |
| 197 | } else if (xVal < d->minX) { |
| 198 | xVal = d->minX; |
| 199 | } |
| 200 | |
| 201 | if (yVal > d->maxY) { |
| 202 | yVal = d->maxY; |
| 203 | } else if (yVal < d->minY) { |
| 204 | yVal = d->minY; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | void KXYSelector::setPosition(int xp, int yp) |
| 209 | { |
| 210 | int w = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth); |
| 211 | |
| 212 | if (xp < w) { |
| 213 | xp = w; |
| 214 | } else if (xp > width() - w) { |
| 215 | xp = width() - w; |
| 216 | } |
| 217 | |
| 218 | if (yp < w) { |
| 219 | yp = w; |
| 220 | } else if (yp > height() - w) { |
| 221 | yp = height() - w; |
| 222 | } |
| 223 | |
| 224 | d->px = xp; |
| 225 | d->py = yp; |
| 226 | |
| 227 | update(); |
| 228 | } |
| 229 | |
| 230 | void KXYSelector::drawContents(QPainter *) |
| 231 | { |
| 232 | } |
| 233 | |
| 234 | void KXYSelector::drawMarker(QPainter *p, int xp, int yp) |
| 235 | { |
| 236 | QPen pen(d->m_markerColor); |
| 237 | p->setPen(pen); |
| 238 | |
| 239 | /* |
| 240 | p->drawLine( xp - 6, yp - 6, xp - 2, yp - 2 ); |
| 241 | p->drawLine( xp - 6, yp + 6, xp - 2, yp + 2 ); |
| 242 | p->drawLine( xp + 6, yp - 6, xp + 2, yp - 2 ); |
| 243 | p->drawLine( xp + 6, yp + 6, xp + 2, yp + 2 ); |
| 244 | */ |
| 245 | p->drawEllipse(x: xp - 4, y: yp - 4, w: 8, h: 8); |
| 246 | } |
| 247 | |
| 248 | #include "moc_kxyselector.cpp" |
| 249 | |