File indexing completed on 2024-05-12 04:44:33
0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com> 0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT 0003 0004 // Own headers 0005 // First the interface, which forces the header to be self-contained. 0006 #include "gradientslider.h" 0007 // Second, the private implementation. 0008 #include "gradientslider_p.h" // IWYU pragma: associated 0009 0010 #include "abstractdiagram.h" 0011 #include "constpropagatingrawpointer.h" 0012 #include "constpropagatinguniquepointer.h" 0013 #include "gradientimageparameters.h" 0014 #include "helperconstants.h" 0015 #include "lchadouble.h" 0016 #include <helper.h> 0017 #include <qevent.h> 0018 #include <qguiapplication.h> 0019 #include <qimage.h> 0020 #include <qpainter.h> 0021 #include <qpen.h> 0022 #include <qpoint.h> 0023 #include <qsharedpointer.h> 0024 #include <qsizepolicy.h> 0025 #include <qtransform.h> 0026 #include <qwidget.h> 0027 0028 namespace PerceptualColor 0029 { 0030 /** @brief Constructs a vertical slider. 0031 * @param colorSpace The color space within which this widget should operate. 0032 * Can be created with @ref RgbColorSpaceFactory. 0033 * Can be created with @ref RgbColorSpaceFactory. 0034 * @param parent parent widget (if any) */ 0035 GradientSlider::GradientSlider(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, QWidget *parent) 0036 : AbstractDiagram(parent) 0037 , d_pointer(new GradientSliderPrivate(this)) 0038 { 0039 d_pointer->initialize(colorSpace, Qt::Orientation::Vertical); 0040 } 0041 0042 /** @brief Constructs a slider. 0043 * @param colorSpace The color space within which this widget should operate. 0044 * Can be created with @ref RgbColorSpaceFactory. 0045 * @param orientation The orientation parameter determines whether 0046 * the slider is horizontal or vertical; the valid values 0047 * are <tt>Qt::Vertical</tt> and <tt>Qt::Horizontal</tt>. 0048 * @param parent parent widget (if any) */ 0049 GradientSlider::GradientSlider(const QSharedPointer<PerceptualColor::RgbColorSpace> &colorSpace, Qt::Orientation orientation, QWidget *parent) 0050 : AbstractDiagram(parent) 0051 , d_pointer(new GradientSliderPrivate(this)) 0052 { 0053 d_pointer->initialize(colorSpace, orientation); 0054 } 0055 0056 /** @brief Default destructor */ 0057 GradientSlider::~GradientSlider() noexcept 0058 { 0059 } 0060 0061 /** @brief Constructor 0062 * 0063 * @param backLink Pointer to the object from which <em>this</em> object 0064 * is the private implementation. */ 0065 GradientSliderPrivate::GradientSliderPrivate(GradientSlider *backLink) 0066 : m_firstColor{0, 0, 0, 0} // dummy value 0067 , m_secondColor{0, 0, 0, 0} // dummy value 0068 , q_pointer(backLink) 0069 { 0070 } 0071 0072 /** @brief Basic initialization. 0073 * 0074 * Code that is shared between the various overloaded constructors 0075 * of @ref GradientSlider. 0076 * 0077 * @note This function requires that @ref q_pointer points to a completely 0078 * initialized object. Therefore, this function may <em>not</em> be called 0079 * within the constructor of @ref GradientSliderPrivate because in this 0080 * moment the @ref GradientSlider object is still not fully initialized. 0081 * However, a call from the <em>function body</em> of a constructor of 0082 * @ref GradientSlider should be okay. 0083 * 0084 * @param colorSpace the color space 0085 * @param orientation determines whether the slider is horizontal or 0086 * vertical */ 0087 void GradientSliderPrivate::initialize(const QSharedPointer<RgbColorSpace> &colorSpace, Qt::Orientation orientation) 0088 { 0089 q_pointer->setFocusPolicy(Qt::StrongFocus); 0090 m_gradientImageParameters.rgbColorSpace = colorSpace; 0091 setOrientationWithoutSignalAndForceNewSizePolicy(orientation); 0092 constexpr LchaDouble first{75, 65, 90, 1}; 0093 constexpr LchaDouble second{50, 75, 45, 1}; 0094 q_pointer->setColors(first, second); 0095 0096 // Connections 0097 q_pointer->connect( // 0098 &m_gradientImage, // 0099 &AsyncImageProvider<GradientImageParameters>::interlacingPassCompleted, // 0100 q_pointer, 0101 &GradientSlider::callUpdate); 0102 } 0103 0104 // No documentation here (documentation of properties 0105 // and its getters are in the header) 0106 LchaDouble GradientSlider::firstColor() const 0107 { 0108 return d_pointer->m_firstColor; 0109 } 0110 0111 /** @brief Setter for @ref firstColor property. 0112 * 0113 * @param newFirstColor the new @ref firstColor */ 0114 void GradientSlider::setFirstColor(const PerceptualColor::LchaDouble &newFirstColor) 0115 { 0116 if (!d_pointer->m_firstColor.hasSameCoordinates(newFirstColor)) { 0117 d_pointer->m_firstColor = newFirstColor; 0118 d_pointer->m_gradientImageParameters.setFirstColor(newFirstColor); 0119 d_pointer->m_gradientImage.setImageParameters( // 0120 d_pointer->m_gradientImageParameters); 0121 update(); 0122 Q_EMIT firstColorChanged(newFirstColor); 0123 } 0124 } 0125 0126 // No documentation here (documentation of properties 0127 // and its getters are in the header) 0128 LchaDouble GradientSlider::secondColor() const 0129 { 0130 return d_pointer->m_secondColor; 0131 } 0132 0133 /** @brief Setter for @ref secondColor property. 0134 * 0135 * @param newSecondColor the new @ref secondColor */ 0136 void GradientSlider::setSecondColor(const PerceptualColor::LchaDouble &newSecondColor) 0137 { 0138 if (!d_pointer->m_secondColor.hasSameCoordinates(newSecondColor)) { 0139 d_pointer->m_secondColor = newSecondColor; 0140 d_pointer->m_gradientImageParameters.setSecondColor(newSecondColor); 0141 d_pointer->m_gradientImage.setImageParameters( // 0142 d_pointer->m_gradientImageParameters); 0143 update(); 0144 Q_EMIT secondColorChanged(newSecondColor); 0145 } 0146 } 0147 0148 /** @brief Setter for both, @ref firstColor property and @ref secondColor 0149 * property. 0150 * 0151 * @param newFirstColor the new @ref firstColor 0152 * @param newSecondColor the new @ref secondColor */ 0153 void GradientSlider::setColors(const PerceptualColor::LchaDouble &newFirstColor, const PerceptualColor::LchaDouble &newSecondColor) 0154 { 0155 setFirstColor(newFirstColor); 0156 setSecondColor(newSecondColor); 0157 update(); 0158 } 0159 0160 /** @brief React on a resize event. 0161 * 0162 * Reimplemented from base class. 0163 * 0164 * @param event The corresponding resize event */ 0165 void GradientSlider::resizeEvent(QResizeEvent *event) 0166 { 0167 Q_UNUSED(event) 0168 d_pointer->m_gradientImageParameters.setGradientLength( // 0169 d_pointer->physicalPixelLength()); 0170 d_pointer->m_gradientImageParameters.setGradientThickness( 0171 // Normally, this should not change, but maybe on Hight-DPI 0172 // devices there might be some differences. 0173 d_pointer->physicalPixelThickness()); 0174 d_pointer->m_gradientImage.setImageParameters( // 0175 d_pointer->m_gradientImageParameters); 0176 update(); 0177 } 0178 0179 /** @brief Recommended size for the widget 0180 * 0181 * Reimplemented from base class. 0182 * 0183 * @returns Recommended size for the widget. 0184 * 0185 * @sa @ref sizeHint() */ 0186 QSize GradientSlider::sizeHint() const 0187 { 0188 QSize result = minimumSizeHint(); 0189 if (d_pointer->m_orientation == Qt::Orientation::Horizontal) { 0190 result.setWidth( // 0191 qRound(result.width() * scaleFromMinumumSizeHintToSizeHint)); 0192 } else { 0193 result.setHeight( // 0194 qRound(result.height() * scaleFromMinumumSizeHintToSizeHint)); 0195 } 0196 return result; 0197 } 0198 0199 /** @brief Recommended minimum size for the widget. 0200 * 0201 * Reimplemented from base class. 0202 * 0203 * @returns Recommended minimum size for the widget. 0204 * 0205 * @sa @ref minimumSizeHint() */ 0206 QSize GradientSlider::minimumSizeHint() const 0207 { 0208 QSize result; 0209 if (d_pointer->m_orientation == Qt::Orientation::Horizontal) { 0210 result.setWidth(gradientMinimumLength()); 0211 result.setHeight(gradientThickness()); 0212 } else { 0213 result.setWidth(gradientThickness()); 0214 result.setHeight(gradientMinimumLength()); 0215 } 0216 return result; 0217 } 0218 0219 // No documentation here (documentation of properties 0220 // and its getters are in the header) 0221 qreal GradientSlider::singleStep() const 0222 { 0223 return d_pointer->m_singleStep; 0224 } 0225 0226 /** @brief Setter for @ref singleStep property. 0227 * 0228 * @param newSingleStep the new @ref singleStep. Is bound to the valid 0229 * range of the property. */ 0230 void GradientSlider::setSingleStep(qreal newSingleStep) 0231 { 0232 // Do not use negative value 0233 const qreal boundedSingleStep = qBound<qreal>(0.0, newSingleStep, 1.0); 0234 if (boundedSingleStep != d_pointer->m_singleStep) { 0235 d_pointer->m_singleStep = boundedSingleStep; 0236 Q_EMIT singleStepChanged(d_pointer->m_singleStep); 0237 } 0238 } 0239 0240 // No documentation here (documentation of properties 0241 // and its getters are in the header) 0242 qreal GradientSlider::pageStep() const 0243 { 0244 return d_pointer->m_pageStep; 0245 } 0246 0247 /** @brief Setter for @ref pageStep property. 0248 * 0249 * @param newPageStep the new @ref pageStep. Is bound to the valid 0250 * range of the property. */ 0251 void GradientSlider::setPageStep(qreal newPageStep) 0252 { 0253 // Do not use negative altkluge 0254 const qreal boundedNewPageStep = qBound<qreal>(0.0, newPageStep, 1.0); 0255 if (boundedNewPageStep != d_pointer->m_pageStep) { 0256 d_pointer->m_pageStep = boundedNewPageStep; 0257 Q_EMIT pageStepChanged(d_pointer->m_pageStep); 0258 } 0259 } 0260 0261 // No documentation here (documentation of properties 0262 // and its getters are in the header) 0263 qreal GradientSlider::value() const 0264 { 0265 return d_pointer->m_value; 0266 } 0267 0268 /** @brief Setter for @ref value property. 0269 * 0270 * @param newValue the new @ref value. Is bound to the valid 0271 * range of the property. */ 0272 void GradientSlider::setValue(qreal newValue) 0273 { 0274 qreal temp = qBound<qreal>(0, newValue, 1); 0275 if (d_pointer->m_value != temp) { 0276 d_pointer->m_value = temp; 0277 update(); 0278 Q_EMIT valueChanged(temp); 0279 } 0280 } 0281 0282 /** @brief React on a mouse press event. 0283 * 0284 * Reimplemented from base class. 0285 * 0286 * @param event The corresponding mouse event */ 0287 void GradientSlider::mousePressEvent(QMouseEvent *event) 0288 { 0289 setValue(d_pointer->fromWidgetPixelPositionToValue(event->pos())); 0290 } 0291 0292 /** @brief React on a mouse release event. 0293 * 0294 * Reimplemented from base class. 0295 * 0296 * @param event The corresponding mouse event */ 0297 void GradientSlider::mouseReleaseEvent(QMouseEvent *event) 0298 { 0299 setValue(d_pointer->fromWidgetPixelPositionToValue(event->pos())); 0300 } 0301 0302 /** @brief React on a mouse move event. 0303 * 0304 * Reimplemented from base class. 0305 * 0306 * @param event The corresponding mouse event */ 0307 void GradientSlider::mouseMoveEvent(QMouseEvent *event) 0308 { 0309 setValue(d_pointer->fromWidgetPixelPositionToValue(event->pos())); 0310 } 0311 0312 /** @brief React on a mouse wheel event. 0313 * 0314 * Reimplemented from base class. 0315 * 0316 * @param event The corresponding mouse event */ 0317 void GradientSlider::wheelEvent(QWheelEvent *event) 0318 { 0319 qreal steps = standardWheelStepCount(event); 0320 // Only react on good old vertical wheels, and not on horizontal wheels 0321 if (steps != 0) { 0322 qreal stepSize; 0323 if ( // 0324 QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) // 0325 || QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier) // 0326 ) { 0327 stepSize = pageStep(); 0328 } else { 0329 stepSize = singleStep(); 0330 } 0331 setValue(d_pointer->m_value + steps * stepSize); 0332 } else { 0333 // Don’t accept the event and let it up to the default treatment: 0334 event->ignore(); 0335 } 0336 } 0337 0338 /** @brief React on key press events. 0339 * 0340 * Reimplemented from base class. 0341 * 0342 * The user can change the @ref value of this widget by the following 0343 * key strokes: 0344 * 0345 * - Qt::Key_Up and Qt::Key_Plus increments a @ref singleStep. 0346 * - Qt::Key_Down and Qt::Key_Minus decrements a @ref singleStep. 0347 * - Qt::Key_Left increments and Qt::Key_Right increment or decrement 0348 * a @ref singleStep, depending on the layout direction (LTR or RTL). 0349 * - Qt::Key_PageUp increments a @ref pageStep 0350 * - Qt::Key_PageDown decrements a @ref pageStep 0351 * - Qt::Key_Home increments to the maximum @ref value 0352 * - Qt::Key_End decrements to the minimum @ref value 0353 * 0354 * @param event the event */ 0355 void GradientSlider::keyPressEvent(QKeyEvent *event) 0356 { 0357 switch (event->key()) { 0358 case Qt::Key_Up: 0359 case Qt::Key_Plus: 0360 setValue(d_pointer->m_value + d_pointer->m_singleStep); 0361 break; 0362 case Qt::Key_Down: 0363 case Qt::Key_Minus: 0364 setValue(d_pointer->m_value - d_pointer->m_singleStep); 0365 break; 0366 case Qt::Key_Left: 0367 if (layoutDirection() == Qt::LayoutDirection::LeftToRight) { 0368 setValue(d_pointer->m_value - d_pointer->m_singleStep); 0369 } else { 0370 setValue(d_pointer->m_value + d_pointer->m_singleStep); 0371 } 0372 break; 0373 case Qt::Key_Right: 0374 if (layoutDirection() == Qt::LayoutDirection::LeftToRight) { 0375 setValue(d_pointer->m_value + d_pointer->m_singleStep); 0376 } else { 0377 setValue(d_pointer->m_value - d_pointer->m_singleStep); 0378 } 0379 break; 0380 case Qt::Key_PageUp: 0381 setValue(d_pointer->m_value + d_pointer->m_pageStep); 0382 break; 0383 case Qt::Key_PageDown: 0384 setValue(d_pointer->m_value - d_pointer->m_pageStep); 0385 break; 0386 case Qt::Key_Home: 0387 setValue(0); 0388 break; 0389 case Qt::Key_End: 0390 setValue(1); 0391 break; 0392 default: 0393 /* Quote from Qt documentation: 0394 * 0395 * “If you reimplement this handler, it is very important that 0396 * you call the base class implementation if you do not act 0397 * upon the key. 0398 * 0399 * The default implementation closes popup widgets if the 0400 * user presses the key sequence for QKeySequence::Cancel 0401 * (typically the Escape key). Otherwise the event is 0402 * ignored, so that the widget’s parent can interpret it.“ */ 0403 QWidget::keyPressEvent(event); 0404 } 0405 } 0406 0407 // No documentation here (documentation of properties 0408 // and its getters are in the header) 0409 Qt::Orientation GradientSlider::orientation() const 0410 { 0411 return d_pointer->m_orientation; 0412 } 0413 0414 /** @brief Forces a new orientation and a corresponding size policy. 0415 * 0416 * @param newOrientation The new orientation for the widget. 0417 * 0418 * @post The new orientation is stored. The signal 0419 * @ref GradientSlider::orientationChanged is <em>not</em> emitted. 0420 * The <tt>sizePolicy</tt> property is updated corresponding to the 0421 * <em>new</em> orientation; this happens even if the new 0422 * orientation is identical to the old @ref m_orientation! */ 0423 void GradientSliderPrivate::setOrientationWithoutSignalAndForceNewSizePolicy(Qt::Orientation newOrientation) 0424 { 0425 if (newOrientation == Qt::Orientation::Vertical) { 0426 q_pointer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); 0427 } else { 0428 q_pointer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 0429 } 0430 m_orientation = newOrientation; 0431 m_gradientImageParameters.setGradientLength(physicalPixelLength()); 0432 m_gradientImageParameters.setGradientThickness( 0433 // Normally, this should not change, but maybe on Hight-DPI 0434 // devices there are some differences. 0435 physicalPixelThickness()); 0436 m_gradientImage.setImageParameters(m_gradientImageParameters); 0437 // Notify the layout system the the geometry has changed 0438 q_pointer->updateGeometry(); 0439 q_pointer->update(); 0440 } 0441 0442 /** @brief Setter for @ref orientation property. 0443 * 0444 * @param newOrientation the new @ref orientation. */ 0445 void GradientSlider::setOrientation(Qt::Orientation newOrientation) 0446 { 0447 if (newOrientation != d_pointer->m_orientation) { 0448 d_pointer->setOrientationWithoutSignalAndForceNewSizePolicy( // 0449 newOrientation); 0450 Q_EMIT orientationChanged(d_pointer->m_orientation); 0451 } 0452 } 0453 0454 /** @brief The rounded length of the widget 0455 * measured in <em>physical pixels</em>. 0456 * 0457 * @returns The rounded length of the widget 0458 * measured in <em>physical pixels</em>. 0459 * 0460 * This is a convenience function to access 0461 * @ref GradientSlider::physicalPixelSize(). The length is the 0462 * size of the widget in the direction of the gradient. 0463 * 0464 * @sa @ref physicalPixelThickness() */ 0465 int GradientSliderPrivate::physicalPixelLength() const 0466 { 0467 if (m_orientation == Qt::Orientation::Vertical) { 0468 return q_pointer->physicalPixelSize().height(); 0469 } else { 0470 return q_pointer->physicalPixelSize().width(); 0471 } 0472 } 0473 0474 /** @brief The rounded thickness of the widget 0475 * measured in <em>physical pixels</em>. 0476 * 0477 * @returns The rounded thickness of the widget 0478 * measured in <em>physical pixels</em>. 0479 * 0480 * This is a convenience function to access 0481 * @ref GradientSlider::physicalPixelSize(). The thickness is the 0482 * size of the widget orthogonal to the direction of the gradient. 0483 * 0484 * @sa @ref physicalPixelLength() */ 0485 int GradientSliderPrivate::physicalPixelThickness() const 0486 { 0487 if (m_orientation == Qt::Orientation::Horizontal) { 0488 return q_pointer->physicalPixelSize().height(); 0489 } else { 0490 return q_pointer->physicalPixelSize().width(); 0491 } 0492 } 0493 0494 /** @brief Converts widget pixel positions to @ref GradientSlider::value 0495 * @param pixelPosition The position of a pixel of the widget coordinate 0496 * system. The given value does not necessarily need to 0497 * be within the actual displayed widget. It might even be negative. 0498 * @returns The corresponding @ref GradientSlider::value for the (center of 0499 * the) given widget pixel position. 0500 * @sa @ref measurementdetails */ 0501 qreal GradientSliderPrivate::fromWidgetPixelPositionToValue(QPoint pixelPosition) 0502 { 0503 // We are interested in the point in the middle of the given pixel. 0504 const QPointF coordinatePoint = pixelPosition + QPointF(0.5, 0.5); 0505 qreal temp; 0506 if (m_orientation == Qt::Orientation::Vertical) { 0507 temp = (q_pointer->size().height() - coordinatePoint.y()) // 0508 / static_cast<qreal>(q_pointer->size().height()); 0509 } else { 0510 if (q_pointer->layoutDirection() == Qt::LayoutDirection::LeftToRight) { 0511 temp = coordinatePoint.x() // 0512 / static_cast<qreal>(q_pointer->size().width()); 0513 } else { 0514 temp = (q_pointer->size().width() - coordinatePoint.x()) // 0515 / static_cast<qreal>(q_pointer->size().width()); 0516 } 0517 } 0518 return qBound<qreal>(0, temp, 1); 0519 } 0520 0521 /** @brief Paint the widget. 0522 * 0523 * Reimplemented from base class. 0524 * 0525 * @param event the paint event */ 0526 void GradientSlider::paintEvent(QPaintEvent *event) 0527 { 0528 Q_UNUSED(event) 0529 // We do not paint directly on the widget, but on a QImage buffer first: 0530 // Render anti-aliased looks better. But as Qt documentation says: 0531 // 0532 // “Renderhints are used to specify flags to QPainter that may or 0533 // may not be respected by any given engine.” 0534 // 0535 // Painting here directly on the widget might lead to different 0536 // anti-aliasing results depending on the underlying window system. This 0537 // is especially problematic as anti-aliasing might shift or not a pixel 0538 // to the left or to the right. So we paint on a QImage first. As QImage 0539 // (at difference to QPixmap and a QWidget) is independent of native 0540 // platform rendering, it guarantees identical anti-aliasing results on 0541 // all platforms. Here the quote from QPainter class documentation: 0542 // 0543 // “To get the optimal rendering result using QPainter, you should 0544 // use the platform independent QImage as paint device; i.e. using 0545 // QImage will ensure that the result has an identical pixel 0546 // representation on any platform.” 0547 QImage paintBuffer; 0548 0549 // Paint the gradient itself. 0550 // Make sure the image will be correct. We set length and thickness, 0551 // just to be sure (we might have missed a resize event). Also, 0552 // the device pixel ratio float might have changed because the 0553 // window has been moved to another screen. We do not update the 0554 // first and the second color because we have complete control 0555 // about these values and are sure the any changes have yet been 0556 // applied. 0557 d_pointer->m_gradientImageParameters.setDevicePixelRatioF( // 0558 devicePixelRatioF()); 0559 d_pointer->m_gradientImageParameters.setGradientLength( // 0560 d_pointer->physicalPixelLength()); 0561 d_pointer->m_gradientImageParameters.setGradientThickness( 0562 // Normally, this should not change, but maybe on Hight-DPI 0563 // devices there are some differences. 0564 d_pointer->physicalPixelThickness()); 0565 d_pointer->m_gradientImage.setImageParameters( // 0566 d_pointer->m_gradientImageParameters); 0567 d_pointer->m_gradientImage.refreshAsync(); 0568 paintBuffer = d_pointer->m_gradientImage.getCache(); 0569 if (paintBuffer.isNull()) { 0570 return; 0571 } 0572 0573 // Draw slider handle 0574 QPainter bufferPainter(&paintBuffer); 0575 // We use antialiasing. As our current handle is just a horizontal or 0576 // vertical line, it might be slightly sharper without antialiasing. 0577 // But all other widgets of this library WILL USE antialiasing because 0578 // their handles are not perfectly horizontal or vertical and without 0579 // antialiasing they might look terrible. Now, when antialiasing is NOT 0580 // used, the line thickness is rounded. This would lead to a different 0581 // thickness in this widget compared to the other widgets. This is not 0582 // a good idea. Therefore, we USE antialiasing here. Anyway, in practical 0583 // tests, it seems almost as sharp as without antialiasing, and 0584 // additionally the position is more exact! 0585 bufferPainter.setRenderHint(QPainter::Antialiasing, true); 0586 QPen pen; 0587 const qreal handleCoordinatePoint = d_pointer->physicalPixelLength() // 0588 / devicePixelRatioF() // 0589 * d_pointer->m_value; 0590 if (hasFocus()) { 0591 pen.setWidthF(handleOutlineThickness() * 3); 0592 pen.setColor(focusIndicatorColor()); 0593 bufferPainter.setPen(pen); 0594 bufferPainter.drawLine(QPointF(handleCoordinatePoint, 0), // 0595 QPointF(handleCoordinatePoint, gradientThickness())); 0596 } 0597 pen.setWidthF(handleOutlineThickness()); 0598 pen.setColor( // 0599 handleColorFromBackgroundLightness( // 0600 d_pointer->m_gradientImageParameters 0601 .colorFromValue( // 0602 d_pointer->m_value) 0603 .l)); 0604 bufferPainter.setPen(pen); 0605 bufferPainter.drawLine(QPointF(handleCoordinatePoint, 0), // 0606 QPointF(handleCoordinatePoint, gradientThickness())); 0607 0608 // Paint the buffer to the actual widget 0609 QTransform transform; 0610 // The m_gradientImageProvider contains the gradient always 0611 // in a default form, independent of the actual orientation 0612 // of this widget and independent of its actual layout direction: 0613 // In the default form, the first color is always on the left, and the 0614 // second color is always on the right. To paint it, we have to 0615 // rotate it if our actual orientation is vertical. And we have to 0616 // mirror it when our actual layout direction is RTL. 0617 if (d_pointer->m_orientation == Qt::Orientation::Vertical) { 0618 if (layoutDirection() == Qt::LayoutDirection::RightToLeft) { 0619 // Even on vertical gradients, we mirror the image, so that 0620 // the well-aligned edge of the transparency background is 0621 // always aligned according to the writing direction. 0622 transform.scale(-1, 1); 0623 transform.rotate(270); 0624 transform.translate(size().height() * (-1), size().width() * (-1)); 0625 } else { 0626 transform.rotate(270); 0627 transform.translate(size().height() * (-1), 0); 0628 } 0629 } else { 0630 if (layoutDirection() == Qt::LayoutDirection::RightToLeft) { 0631 transform.scale(-1, 1); 0632 transform.translate(size().width() * (-1), 0); 0633 } 0634 } 0635 QPainter widgetPainter(this); 0636 widgetPainter.setTransform(transform); 0637 widgetPainter.drawImage(0, 0, paintBuffer); 0638 0639 // // TODO Draw a focus rectangle like this?: 0640 // widgetPainter.setTransform(QTransform()); 0641 // if (hasFocus()) { 0642 // QStyleOptionFocusRect opt; 0643 // opt.palette = palette(); 0644 // opt.rect = rect(); 0645 // opt.state = QStyle::State_None | 0646 // QStyle::State_KeyboardFocusChange; style()->drawPrimitive( 0647 // QStyle::PE_FrameFocusRect, 0648 // &opt, 0649 // &widgetPainter, 0650 // this 0651 // ); 0652 // } 0653 } 0654 0655 } // namespace PerceptualColor