Warning, file /education/kmplot/kmplot/kgradientdialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 KmPlot - a math. function plotter for the KDE-Desktop 0003 0004 SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org> 0005 0006 This file is part of the KDE Project. 0007 KmPlot is part of the KDE-EDU Project. 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 0011 */ 0012 0013 #include "kgradientdialog.h" 0014 0015 #include <KLocalizedString> 0016 0017 #include <QApplication> 0018 #include <QColorDialog> 0019 #include <QDialogButtonBox> 0020 #include <QLabel> 0021 #include <QLinearGradient> 0022 #include <QPaintEvent> 0023 #include <QPainter> 0024 #include <QPointer> 0025 #include <QStyleOption> 0026 #include <QStyleOptionButton> 0027 #include <QTileRules> 0028 #include <QVBoxLayout> 0029 0030 const double SQRT_3 = 1.732050808; 0031 const double ArrowLength = 8; 0032 const double ArrowHalfWidth = ArrowLength / SQRT_3; 0033 0034 // BEGIN class KGradientEditor 0035 KGradientEditor::KGradientEditor(QWidget *parent) 0036 : QWidget(parent) 0037 { 0038 m_haveArrow = false; 0039 m_clickOffset = 0; 0040 m_orientation = Qt::Horizontal; 0041 findGradientStop(); 0042 } 0043 0044 KGradientEditor::~KGradientEditor() 0045 { 0046 } 0047 0048 void KGradientEditor::setGradient(const QGradient &gradient) 0049 { 0050 if (m_gradient == gradient) 0051 return; 0052 setGradient(gradient.stops()); 0053 findGradientStop(); 0054 } 0055 0056 void KGradientEditor::setColor(const QColor &color) 0057 { 0058 // Hmm...why doesn't qvector have some sortof search / replace functionality? 0059 QGradientStops stops = m_gradient.stops(); 0060 for (int i = 0; i < stops.size(); ++i) { 0061 if (stops[i] != m_currentStop) 0062 continue; 0063 0064 if (stops[i].second == color) 0065 return; 0066 0067 m_currentStop.second = color; 0068 stops[i] = m_currentStop; 0069 break; 0070 } 0071 0072 setGradient(stops); 0073 } 0074 0075 QSize KGradientEditor::minimumSizeHint() const 0076 { 0077 double w = 3 * ArrowHalfWidth; 0078 double h = 12 + ArrowLength; 0079 0080 if (m_orientation == Qt::Vertical) 0081 qSwap(w, h); 0082 0083 return QSizeF(w, h).toSize(); 0084 } 0085 0086 void KGradientEditor::paintEvent(QPaintEvent *) 0087 { 0088 QPainter painter(this); 0089 0090 // BEGIN draw gradient 0091 QRectF r; 0092 QLinearGradient lg; 0093 0094 if (m_orientation == Qt::Horizontal) { 0095 lg = QLinearGradient(0, 0, width(), 0); 0096 r = QRectF(ArrowHalfWidth - 1, 0, width() - 2 * ArrowHalfWidth + 1, height() - ArrowLength); 0097 } else { 0098 lg = QLinearGradient(0, 0, 0, height()); 0099 r = QRectF(0, ArrowHalfWidth - 1, width() - ArrowLength, height() - 2 * ArrowHalfWidth + 1); 0100 } 0101 0102 lg.setStops(m_gradient.stops()); 0103 painter.setBrush(lg); 0104 painter.setPen(QPen(Qt::black, 1)); 0105 painter.drawRect(r); 0106 // END draw gradient 0107 0108 // BEGIN draw arrows 0109 painter.setRenderHint(QPainter::Antialiasing, true); 0110 const QGradientStops stops = m_gradient.stops(); 0111 for (const QGradientStop &stop : stops) 0112 drawArrow(&painter, stop); 0113 // END draw arrows 0114 } 0115 0116 void KGradientEditor::drawArrow(QPainter *painter, const QGradientStop &stop) 0117 { 0118 QPolygonF arrow(3); 0119 0120 double mid = toArrowPos(stop.first); 0121 0122 if (m_orientation == Qt::Horizontal) { 0123 arrow[0] = QPointF(mid, height() - ArrowLength + 0.5); 0124 arrow[1] = QPointF(mid + ArrowHalfWidth, height() - 0.5); 0125 arrow[2] = QPointF(mid - ArrowHalfWidth, height() - 0.5); 0126 } else { 0127 arrow[0] = QPointF(width() - ArrowLength + 0.5, mid); 0128 arrow[1] = QPointF(width() - 0.5, mid + ArrowHalfWidth); 0129 arrow[2] = QPointF(width() - 0.5, mid - ArrowHalfWidth); 0130 } 0131 0132 bool selected = (stop == m_currentStop); 0133 QColor color(selected ? palette().color(QPalette::Dark) : Qt::black); 0134 0135 painter->setPen(color); 0136 painter->setBrush(stop.second); 0137 painter->drawPolygon(arrow); 0138 } 0139 0140 void KGradientEditor::contextMenuEvent(QContextMenuEvent *e) 0141 { 0142 // Prevent the "QWhatsThis" menu from popping up when right-clicking 0143 e->accept(); 0144 } 0145 0146 void KGradientEditor::removeStop() 0147 { 0148 QGradientStops stops = m_gradient.stops(); 0149 for (int i = 0; i < stops.size(); ++i) { 0150 if (stops[i] != m_currentStop) 0151 continue; 0152 0153 stops.remove(i); 0154 break; 0155 } 0156 0157 setGradient(stops); 0158 findGradientStop(); 0159 } 0160 0161 void KGradientEditor::mousePressEvent(QMouseEvent *e) 0162 { 0163 if (!getGradientStop(e->pos())) 0164 return; 0165 e->accept(); 0166 0167 if (e->button() == Qt::RightButton) 0168 removeStop(); 0169 else 0170 m_haveArrow = true; 0171 } 0172 0173 bool KGradientEditor::getGradientStop(const QPoint &point) 0174 { 0175 double dl; // the vertical (for horizontal layout) distance from the tip of the arrows 0176 if (m_orientation == Qt::Horizontal) 0177 dl = point.y() - (height() - ArrowLength); 0178 else 0179 dl = point.x() - (width() - ArrowLength); 0180 0181 // Is the arrow in the strip? 0182 if (dl < 0) 0183 return false; 0184 0185 QGradientStops stops = m_gradient.stops(); 0186 0187 // Iterate over stops in reverse as the last stops are displayed on top of 0188 // the first stops. 0189 for (int i = stops.size() - 1; i >= 0; --i) { 0190 QGradientStop stop = stops[i]; 0191 0192 double pos = toArrowPos(stop.first); 0193 0194 // Is the click inside the arrow? 0195 double lower = pos - dl * (ArrowHalfWidth / ArrowLength); 0196 double upper = pos + dl * (ArrowHalfWidth / ArrowLength); 0197 0198 double x = (m_orientation == Qt::Horizontal) ? point.x() : point.y(); 0199 if (x < lower || x > upper) 0200 continue; 0201 0202 // Is inside arrow! :) 0203 m_clickOffset = x - pos; 0204 0205 setCurrentStop(stop); 0206 return true; 0207 } 0208 0209 return false; 0210 } 0211 0212 void KGradientEditor::mouseMoveEvent(QMouseEvent *e) 0213 { 0214 if (!m_haveArrow) 0215 return; 0216 0217 e->accept(); 0218 QPoint point = e->pos(); 0219 0220 // Hmm...why doesn't qvector have some sortof search / replace functionality? 0221 QGradientStops stops = m_gradient.stops(); 0222 for (int i = 0; i < stops.size(); ++i) { 0223 if (stops[i] != m_currentStop) 0224 continue; 0225 0226 double x = (m_orientation == Qt::Horizontal) ? point.x() : point.y(); 0227 0228 m_currentStop.first = fromArrowPos(x - m_clickOffset); 0229 0230 stops[i] = m_currentStop; 0231 break; 0232 } 0233 0234 setGradient(stops); 0235 } 0236 0237 void KGradientEditor::mouseReleaseEvent(QMouseEvent *) 0238 { 0239 m_haveArrow = false; 0240 } 0241 0242 void KGradientEditor::mouseDoubleClickEvent(QMouseEvent *e) 0243 { 0244 e->accept(); 0245 0246 if (getGradientStop(e->pos())) 0247 return; 0248 0249 // Create new stop 0250 QPoint point = e->pos(); 0251 double pos = fromArrowPos((m_orientation == Qt::Horizontal) ? point.x() : point.y()); 0252 0253 QGradientStop stop; 0254 stop.first = pos; 0255 stop.second = Qt::red; 0256 0257 QGradientStops stops = m_gradient.stops(); 0258 stops << stop; 0259 0260 setGradient(stops); 0261 setCurrentStop(stop); 0262 } 0263 0264 void KGradientEditor::setOrientation(Qt::Orientation orientation) 0265 { 0266 m_orientation = orientation; 0267 update(); 0268 } 0269 0270 void KGradientEditor::findGradientStop() 0271 { 0272 QGradientStops stops = m_gradient.stops(); 0273 0274 // The QGradientStops should always have at least one stop in, since 0275 // QGradient returns a Black->White gradient if its stops are empty. 0276 Q_ASSERT(!stops.isEmpty()); 0277 0278 // Pick a stop in the center 0279 setCurrentStop(stops[stops.size() / 2]); 0280 } 0281 0282 void KGradientEditor::setCurrentStop(const QGradientStop &stop) 0283 { 0284 if (m_currentStop == stop) 0285 return; 0286 0287 bool colorChanged = stop.second != m_currentStop.second; 0288 0289 m_currentStop = stop; 0290 update(); 0291 0292 if (colorChanged) 0293 emit colorSelected(stop.second); 0294 } 0295 0296 void KGradientEditor::setGradient(const QGradientStops &stops) 0297 { 0298 if (stops == m_gradient.stops()) 0299 return; 0300 0301 m_gradient.setStops(stops); 0302 update(); 0303 emit gradientChanged(m_gradient); 0304 } 0305 0306 double KGradientEditor::toArrowPos(double stop) const 0307 { 0308 double l = (m_orientation == Qt::Horizontal) ? width() : height(); 0309 l -= 2 * ArrowHalfWidth; 0310 return stop * l + ArrowHalfWidth; 0311 } 0312 0313 double KGradientEditor::fromArrowPos(double pos) const 0314 { 0315 double l = (m_orientation == Qt::Horizontal) ? width() : height(); 0316 l -= 2 * ArrowHalfWidth; 0317 0318 double stop = (pos - ArrowHalfWidth) / l; 0319 0320 if (stop < 0) 0321 stop = 0; 0322 else if (stop > 1) 0323 stop = 1; 0324 0325 return stop; 0326 } 0327 // END class KGradientEditor 0328 0329 // BEGIN class KGradientDialog 0330 KGradientDialog::KGradientDialog(QWidget *parent, bool modal) 0331 : QDialog(parent) 0332 { 0333 QWidget *widget = new QWidget(this); 0334 m_gradient = new KGradientEditor(widget); 0335 m_colorDialog = new QColorDialog(widget); 0336 m_colorDialog->setWindowFlags(Qt::Widget); 0337 m_colorDialog->setOptions(QColorDialog::DontUseNativeDialog | QColorDialog::NoButtons); 0338 0339 QLabel *label = new QLabel(i18n("(Double-click on the gradient to add a stop)"), widget); 0340 QPushButton *button = new QPushButton(i18n("Remove stop"), widget); 0341 connect(button, &QPushButton::clicked, m_gradient, &KGradientEditor::removeStop); 0342 0343 QDialogButtonBox *buttonBox = new QDialogButtonBox(modal ? QDialogButtonBox::Ok | QDialogButtonBox::Cancel : QDialogButtonBox::Close); 0344 connect(buttonBox, &QDialogButtonBox::accepted, this, &KGradientDialog::accept); 0345 connect(buttonBox, &QDialogButtonBox::rejected, this, &KGradientDialog::reject); 0346 0347 // BEGIN layout widgets 0348 QVBoxLayout *layout = new QVBoxLayout(this); 0349 layout->setContentsMargins(0, 0, 0, 0); 0350 0351 m_gradient->setFixedHeight(24); 0352 layout->addWidget(m_gradient); 0353 0354 QHBoxLayout *hLayout = new QHBoxLayout; 0355 hLayout->addWidget(label); 0356 hLayout->addStretch(1); 0357 hLayout->addWidget(button); 0358 layout->addLayout(hLayout); 0359 layout->addWidget(m_colorDialog); 0360 layout->addWidget(buttonBox); 0361 resize(layout->minimumSize()); 0362 // END layout widgets 0363 0364 setWindowTitle(i18nc("@title:window", "Choose a Gradient")); 0365 0366 setModal(modal); 0367 0368 connect(m_gradient, &KGradientEditor::colorSelected, m_colorDialog, &QColorDialog::setCurrentColor); 0369 connect(m_colorDialog, &QColorDialog::currentColorChanged, m_gradient, &KGradientEditor::setColor); 0370 connect(m_gradient, &KGradientEditor::gradientChanged, this, &KGradientDialog::gradientChanged); 0371 0372 m_colorDialog->setCurrentColor(m_gradient->color()); 0373 } 0374 0375 KGradientDialog::~KGradientDialog() 0376 { 0377 } 0378 0379 // static 0380 int KGradientDialog::getGradient(QGradient &gradient, QWidget *parent) 0381 { 0382 QPointer<KGradientDialog> dlg = new KGradientDialog(parent, true); 0383 dlg->setGradient(gradient); 0384 0385 int result = dlg->exec(); 0386 if (result == Accepted) 0387 gradient = dlg->gradient(); 0388 delete dlg; 0389 return result; 0390 } 0391 0392 void KGradientDialog::setGradient(const QGradient &gradient) 0393 { 0394 m_gradient->setGradient(gradient); 0395 } 0396 0397 QGradient KGradientDialog::gradient() const 0398 { 0399 return m_gradient->gradient(); 0400 } 0401 // END class KGradientDialog 0402 0403 // BEGIN class KGradientButton 0404 KGradientButton::KGradientButton(QWidget *parent) 0405 : QPushButton(parent) 0406 { 0407 connect(this, &KGradientButton::clicked, this, &KGradientButton::chooseGradient); 0408 } 0409 0410 KGradientButton::~KGradientButton() 0411 { 0412 } 0413 0414 void KGradientButton::initStyleOption(QStyleOptionButton *opt) const 0415 { 0416 opt->initFrom(this); 0417 opt->text.clear(); 0418 opt->icon = QIcon(); 0419 opt->features = QStyleOptionButton::None; 0420 } 0421 0422 QSize KGradientButton::sizeHint() const 0423 { 0424 QStyleOptionButton opt; 0425 initStyleOption(&opt); 0426 return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this); 0427 } 0428 0429 void KGradientButton::setGradient(const QGradient &gradient) 0430 { 0431 if (m_gradient.stops() == gradient.stops()) 0432 return; 0433 0434 m_gradient.setStops(gradient.stops()); 0435 emit gradientChanged(m_gradient); 0436 } 0437 0438 void KGradientButton::chooseGradient() 0439 { 0440 int result = KGradientDialog::getGradient(m_gradient, this); 0441 if (result == KGradientDialog::Accepted) 0442 emit gradientChanged(m_gradient); 0443 } 0444 0445 void KGradientButton::paintEvent(QPaintEvent *) 0446 { 0447 // Mostly copied verbatim from KColorButton - thanks! :) 0448 0449 QPainter painter(this); 0450 0451 // First, we need to draw the bevel. 0452 QStyleOptionButton butOpt; 0453 initStyleOption(&butOpt); 0454 style()->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this); 0455 0456 // OK, now we can muck around with drawing out pretty little color box 0457 // First, sort out where it goes 0458 QRect labelRect = style()->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this); 0459 int shift = style()->pixelMetric(QStyle::PM_ButtonMargin); 0460 labelRect.adjust(shift, shift, -shift, -shift); 0461 int x, y, w, h; 0462 labelRect.getRect(&x, &y, &w, &h); 0463 0464 if (isChecked() || isDown()) { 0465 x += style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal); 0466 y += style()->pixelMetric(QStyle::PM_ButtonShiftVertical); 0467 } 0468 0469 qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, NULL); 0470 0471 if (isEnabled()) { 0472 QLinearGradient lg(x + 1, 0, x + w - 1, 0); 0473 lg.setStops(m_gradient.stops()); 0474 painter.setBrush(lg); 0475 } else 0476 painter.setBrush(palette().color(backgroundRole())); 0477 0478 painter.drawRect(x + 1, y + 1, w - 2, h - 2); 0479 0480 if (hasFocus()) { 0481 QRect focusRect = style()->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this); 0482 QStyleOptionFocusRect focusOpt; 0483 focusOpt.initFrom(this); 0484 focusOpt.rect = focusRect; 0485 focusOpt.backgroundColor = palette().window().color(); 0486 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this); 0487 } 0488 } 0489 // END class KGradientButton