File indexing completed on 2024-04-28 03:42:25
0001 /* 0002 SPDX-FileCopyrightText: Kitware Inc. 0003 SPDX-License-Identifier: Apache-2.0 0004 0005 Modified from the original code to support 3 sliders, May 2023. 0006 */ 0007 0008 // Qt includes 0009 #include <QDebug> 0010 #include <QMouseEvent> 0011 #include <QKeyEvent> 0012 #include <QStyleOptionSlider> 0013 #include <QApplication> 0014 #include <QStylePainter> 0015 #include <QStyle> 0016 #include <QToolTip> 0017 0018 #include "ctk3slider.h" 0019 0020 class ctk3SliderPrivate 0021 { 0022 Q_DECLARE_PUBLIC(ctk3Slider) 0023 protected: 0024 ctk3Slider* const q_ptr; 0025 public: 0026 /// Boolean indicates the selected handle 0027 /// True for the minimum range handle, false for the maximum range handle 0028 enum Handle 0029 { 0030 NoHandle = 0x0, 0031 MinimumHandle = 0x1, 0032 MidHandle = 0x2, 0033 MaximumHandle = 0x4 0034 }; 0035 Q_DECLARE_FLAGS(Handles, Handle) 0036 0037 ctk3SliderPrivate(ctk3Slider &object); 0038 void init(); 0039 0040 /// Return the handle at the given pos, or none if no handle is at the pos. 0041 /// If a handle is selected, handleRect is set to the handle rect. 0042 /// otherwise return NoHandle and handleRect is set to the combined rect of 0043 /// the min and max handles 0044 Handle handleAtPos(const QPoint &pos, QRect &handleRect)const; 0045 0046 /// Copied verbatim from QSliderPrivate class (see QSlider.cpp) 0047 int pixelPosToRangeValue(int pos) const; 0048 int pixelPosFromRangeValue(int val) const; 0049 0050 /// Draw the bottom and top sliders. 0051 void drawMinimumSlider( QStylePainter* painter ) const; 0052 void drawMaximumSlider( QStylePainter* painter ) const; 0053 void drawMidSlider( QStylePainter* painter ) const; 0054 0055 /// End points of the range on the Model 0056 int m_MaximumValue; 0057 int m_MinimumValue; 0058 int m_MidValue; 0059 0060 /// End points of the range on the GUI. This is synced with the model. 0061 int m_MaximumPosition; 0062 int m_MinimumPosition; 0063 int m_MidPosition; 0064 0065 /// Controls selected ? 0066 QStyle::SubControl m_MinimumSliderSelected; 0067 QStyle::SubControl m_MaximumSliderSelected; 0068 QStyle::SubControl m_MidSliderSelected; 0069 0070 /// See QSliderPrivate::clickOffset. 0071 /// Overrides this ivar 0072 int m_SubclassClickOffset; 0073 0074 /// See QSliderPrivate::position 0075 /// Overrides this ivar. 0076 int m_SubclassPosition; 0077 0078 /// Original width between the 2 bounds before any moves 0079 int m_SubclassWidth; 0080 0081 ctk3SliderPrivate::Handles m_SelectedHandles; 0082 0083 QString m_HandleToolTip; 0084 0085 private: 0086 Q_DISABLE_COPY(ctk3SliderPrivate) 0087 }; 0088 0089 // -------------------------------------------------------------------------- 0090 ctk3SliderPrivate::ctk3SliderPrivate(ctk3Slider &object) 0091 : q_ptr(&object) 0092 { 0093 this->m_MinimumValue = 0; 0094 this->m_MaximumValue = 100; 0095 this->m_MidValue = 50; 0096 this->m_MinimumPosition = 0; 0097 this->m_MaximumPosition = 100; 0098 this->m_MidPosition = 50; 0099 this->m_MinimumSliderSelected = QStyle::SC_None; 0100 this->m_MaximumSliderSelected = QStyle::SC_None; 0101 this->m_MidSliderSelected = QStyle::SC_None; 0102 this->m_SubclassClickOffset = 0; 0103 this->m_SubclassPosition = 0; 0104 this->m_SubclassWidth = 0; 0105 this->m_SelectedHandles = ctk3SliderPrivate::NoHandle; 0106 } 0107 0108 // -------------------------------------------------------------------------- 0109 void ctk3SliderPrivate::init() 0110 { 0111 Q_Q(ctk3Slider); 0112 this->m_MinimumValue = q->minimum(); 0113 this->m_MaximumValue = q->maximum(); 0114 this->m_MaximumValue = (q->minimum() + q->maximum()) / 2.0; 0115 this->m_MinimumPosition = q->minimum(); 0116 this->m_MaximumPosition = q->maximum(); 0117 this->m_MidPosition = (q->minimum() + q->maximum()) / 2.0; 0118 q->connect(q, SIGNAL(rangeChanged(int, int)), q, SLOT(onRangeChanged(int, int))); 0119 } 0120 0121 // -------------------------------------------------------------------------- 0122 ctk3SliderPrivate::Handle ctk3SliderPrivate::handleAtPos(const QPoint &pos, QRect &handleRect)const 0123 { 0124 Q_Q(const ctk3Slider); 0125 0126 QStyleOptionSlider option; 0127 q->initStyleOption( &option ); 0128 0129 // The functions hitTestComplexControl only know about 1 handle. As we have 0130 // 3, we change the position of the handle and test if the pos correspond to 0131 // any of the 3 positions. 0132 0133 // Test the MinimumHandle 0134 option.sliderPosition = this->m_MinimumPosition; 0135 option.sliderValue = this->m_MinimumValue; 0136 0137 QStyle::SubControl minimumControl = q->style()->hitTestComplexControl( 0138 QStyle::CC_Slider, &option, pos, q); 0139 QRect minimumHandleRect = q->style()->subControlRect( 0140 QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q); 0141 0142 // Test if the pos is under the Maximum handle 0143 option.sliderPosition = this->m_MaximumPosition; 0144 option.sliderValue = this->m_MaximumValue; 0145 0146 QStyle::SubControl maximumControl = q->style()->hitTestComplexControl( 0147 QStyle::CC_Slider, &option, pos, q); 0148 QRect maximumHandleRect = q->style()->subControlRect( 0149 QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q); 0150 0151 // Test if the pos is under the Mid handle 0152 option.sliderPosition = this->m_MidPosition; 0153 option.sliderValue = this->m_MidValue; 0154 0155 QStyle::SubControl midControl = q->style()->hitTestComplexControl( 0156 QStyle::CC_Slider, &option, pos, q); 0157 QRect midHandleRect = q->style()->subControlRect( 0158 QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q); 0159 0160 if (minimumControl == QStyle::SC_SliderHandle) 0161 { 0162 handleRect = minimumHandleRect; 0163 return MinimumHandle; 0164 } 0165 else if (midControl == QStyle::SC_SliderHandle) 0166 { 0167 handleRect = midHandleRect; 0168 return MidHandle; 0169 } 0170 else if (maximumControl == QStyle::SC_SliderHandle) 0171 { 0172 handleRect = maximumHandleRect; 0173 return MaximumHandle; 0174 } 0175 handleRect = minimumHandleRect.united(maximumHandleRect); 0176 return NoHandle; 0177 } 0178 0179 // -------------------------------------------------------------------------- 0180 // Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp 0181 // 0182 int ctk3SliderPrivate::pixelPosToRangeValue( int pos ) const 0183 { 0184 Q_Q(const ctk3Slider); 0185 QStyleOptionSlider option; 0186 q->initStyleOption( &option ); 0187 0188 QRect gr = q->style()->subControlRect( QStyle::CC_Slider, 0189 &option, 0190 QStyle::SC_SliderGroove, 0191 q ); 0192 QRect sr = q->style()->subControlRect( QStyle::CC_Slider, 0193 &option, 0194 QStyle::SC_SliderHandle, 0195 q ); 0196 int sliderMin, sliderMax, sliderLength; 0197 if (option.orientation == Qt::Horizontal) 0198 { 0199 sliderLength = sr.width(); 0200 sliderMin = gr.x(); 0201 sliderMax = gr.right() - sliderLength + 1; 0202 } 0203 else 0204 { 0205 sliderLength = sr.height(); 0206 sliderMin = gr.y(); 0207 sliderMax = gr.bottom() - sliderLength + 1; 0208 } 0209 0210 return QStyle::sliderValueFromPosition( q->minimum(), 0211 q->maximum(), 0212 pos - sliderMin, 0213 sliderMax - sliderMin, 0214 option.upsideDown ); 0215 } 0216 0217 //--------------------------------------------------------------------------- 0218 int ctk3SliderPrivate::pixelPosFromRangeValue( int val ) const 0219 { 0220 Q_Q(const ctk3Slider); 0221 QStyleOptionSlider option; 0222 q->initStyleOption( &option ); 0223 0224 QRect gr = q->style()->subControlRect( QStyle::CC_Slider, 0225 &option, 0226 QStyle::SC_SliderGroove, 0227 q ); 0228 QRect sr = q->style()->subControlRect( QStyle::CC_Slider, 0229 &option, 0230 QStyle::SC_SliderHandle, 0231 q ); 0232 int sliderMin, sliderMax, sliderLength; 0233 if (option.orientation == Qt::Horizontal) 0234 { 0235 sliderLength = sr.width(); 0236 sliderMin = gr.x(); 0237 sliderMax = gr.right() - sliderLength + 1; 0238 } 0239 else 0240 { 0241 sliderLength = sr.height(); 0242 sliderMin = gr.y(); 0243 sliderMax = gr.bottom() - sliderLength + 1; 0244 } 0245 0246 return QStyle::sliderPositionFromValue( q->minimum(), 0247 q->maximum(), 0248 val, 0249 sliderMax - sliderMin, 0250 option.upsideDown ) + sliderMin; 0251 } 0252 0253 //--------------------------------------------------------------------------- 0254 // Draw slider at the bottom end of the range 0255 void ctk3SliderPrivate::drawMinimumSlider( QStylePainter* painter ) const 0256 { 0257 Q_Q(const ctk3Slider); 0258 QStyleOptionSlider option; 0259 q->initMinimumSliderStyleOption( &option ); 0260 0261 option.subControls = QStyle::SC_SliderHandle; 0262 option.sliderValue = m_MinimumValue; 0263 option.sliderPosition = m_MinimumPosition; 0264 if (q->isMinimumSliderDown()) 0265 { 0266 option.activeSubControls = QStyle::SC_SliderHandle; 0267 option.state |= QStyle::State_Sunken; 0268 } 0269 #ifdef Q_OS_MAC 0270 // On mac style, drawing just the handle actually draws also the groove. 0271 QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option, 0272 QStyle::SC_SliderHandle, q); 0273 painter->setClipRect(clip); 0274 #endif 0275 painter->drawComplexControl(QStyle::CC_Slider, option); 0276 } 0277 0278 //--------------------------------------------------------------------------- 0279 // Draw slider at the top end of the range 0280 void ctk3SliderPrivate::drawMaximumSlider( QStylePainter* painter ) const 0281 { 0282 Q_Q(const ctk3Slider); 0283 QStyleOptionSlider option; 0284 q->initMaximumSliderStyleOption( &option ); 0285 0286 option.subControls = QStyle::SC_SliderHandle; 0287 option.sliderValue = m_MaximumValue; 0288 option.sliderPosition = m_MaximumPosition; 0289 if (q->isMaximumSliderDown()) 0290 { 0291 option.activeSubControls = QStyle::SC_SliderHandle; 0292 option.state |= QStyle::State_Sunken; 0293 } 0294 #ifdef Q_OS_MAC 0295 // On mac style, drawing just the handle actually draws also the groove. 0296 QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option, 0297 QStyle::SC_SliderHandle, q); 0298 painter->setClipRect(clip); 0299 #endif 0300 painter->drawComplexControl(QStyle::CC_Slider, option); 0301 } 0302 0303 // Draw the mid slider 0304 void ctk3SliderPrivate::drawMidSlider( QStylePainter* painter ) const 0305 { 0306 Q_Q(const ctk3Slider); 0307 QStyleOptionSlider option; 0308 q->initMidSliderStyleOption( &option ); 0309 0310 option.subControls = QStyle::SC_SliderHandle; 0311 option.sliderValue = m_MidValue; 0312 option.sliderPosition = m_MidPosition; 0313 if (q->isMidSliderDown()) 0314 { 0315 option.activeSubControls = QStyle::SC_SliderHandle; 0316 option.state |= QStyle::State_Sunken; 0317 } 0318 #ifdef Q_OS_MAC 0319 // On mac style, drawing just the handle actually draws also the groove. 0320 QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option, 0321 QStyle::SC_SliderHandle, q); 0322 painter->setClipRect(clip); 0323 #endif 0324 painter->drawComplexControl(QStyle::CC_Slider, option); 0325 } 0326 0327 // -------------------------------------------------------------------------- 0328 ctk3Slider::ctk3Slider(QWidget* _parent) 0329 : QSlider(_parent) 0330 , d_ptr(new ctk3SliderPrivate(*this)) 0331 { 0332 Q_D(ctk3Slider); 0333 d->init(); 0334 } 0335 0336 // -------------------------------------------------------------------------- 0337 ctk3Slider::ctk3Slider( Qt::Orientation o, 0338 QWidget* parentObject ) 0339 : QSlider(o, parentObject) 0340 , d_ptr(new ctk3SliderPrivate(*this)) 0341 { 0342 Q_D(ctk3Slider); 0343 d->init(); 0344 } 0345 0346 // -------------------------------------------------------------------------- 0347 ctk3Slider::ctk3Slider(ctk3SliderPrivate* impl, QWidget* _parent) 0348 : QSlider(_parent) 0349 , d_ptr(impl) 0350 { 0351 Q_D(ctk3Slider); 0352 d->init(); 0353 } 0354 0355 // -------------------------------------------------------------------------- 0356 ctk3Slider::ctk3Slider( ctk3SliderPrivate* impl, Qt::Orientation o, 0357 QWidget* parentObject ) 0358 : QSlider(o, parentObject) 0359 , d_ptr(impl) 0360 { 0361 Q_D(ctk3Slider); 0362 d->init(); 0363 } 0364 0365 // -------------------------------------------------------------------------- 0366 ctk3Slider::~ctk3Slider() 0367 { 0368 } 0369 0370 // -------------------------------------------------------------------------- 0371 int ctk3Slider::minimumValue() const 0372 { 0373 Q_D(const ctk3Slider); 0374 return d->m_MinimumValue; 0375 } 0376 0377 // -------------------------------------------------------------------------- 0378 void ctk3Slider::setMinimumValue( int min ) 0379 { 0380 Q_D(ctk3Slider); 0381 // Min cannot be set higher than mid or max 0382 int newMin = qMin( qMin(min, d->m_MidValue), d->m_MaximumValue); 0383 this->setValues( newMin, d->m_MidValue, d->m_MaximumValue); 0384 } 0385 0386 // -------------------------------------------------------------------------- 0387 int ctk3Slider::maximumValue() const 0388 { 0389 Q_D(const ctk3Slider); 0390 return d->m_MaximumValue; 0391 } 0392 0393 // -------------------------------------------------------------------------- 0394 void ctk3Slider::setMaximumValue( int max ) 0395 { 0396 Q_D(ctk3Slider); 0397 // Max cannot be set lower than min or mid 0398 int newMax = qMax( qMax(max, d->m_MidValue), d->m_MinimumValue); 0399 this->setValues(d->m_MinimumValue, d->m_MidValue, newMax); 0400 } 0401 0402 // -------------------------------------------------------------------------- 0403 int ctk3Slider::midValue() const 0404 { 0405 Q_D(const ctk3Slider); 0406 return d->m_MidValue; 0407 } 0408 0409 // -------------------------------------------------------------------------- 0410 void ctk3Slider::setMidValue( int mid ) 0411 { 0412 Q_D(ctk3Slider); 0413 // Mid cannot be set lower than min or higher than max; 0414 int newMid = qMax(d->m_MinimumValue, mid); 0415 newMid = qMin(newMid, d->m_MaximumValue); 0416 this->setValues(d->m_MinimumValue, newMid, d->m_MaximumValue); 0417 } 0418 0419 // -------------------------------------------------------------------------- 0420 namespace 0421 { 0422 // Sorts the 3 numbers so l becomes the least, and u becomes the largest. 0423 void sortLMU(int &l, int &m, int &u) 0424 { 0425 int kL = l, kM = m, kU = u; 0426 if (l <= m && l <= u) // l is the smallest 0427 { 0428 if (m <= u) // order is l,m,u 0429 return; 0430 else // order is l, u, m 0431 { 0432 l = kL; 0433 m = kU; 0434 u = kM; 0435 } 0436 } 0437 else if (m <= l && m <= u) // m is the smallest 0438 { 0439 if (l <= u) // order is m, l, u 0440 { 0441 l = kM; 0442 m = kL; 0443 u = kU; 0444 } 0445 else // order is m, u, l 0446 { 0447 l = kM; 0448 m = kU; 0449 u = kL; 0450 } 0451 } 0452 else // u is the smallest 0453 { 0454 if (l <= m) // order is u, l, m 0455 { 0456 l = kL; 0457 m = kU; 0458 u = kM; 0459 l = m; 0460 } 0461 else // order is u, m, l 0462 { 0463 l = kU; 0464 m = kM; 0465 u = kL; 0466 } 0467 } 0468 } 0469 } 0470 void ctk3Slider::setValues(int l, int m, int u) 0471 { 0472 Q_D(ctk3Slider); 0473 sortLMU(l, m, u); 0474 const int minValue = 0475 qBound(this->minimum(), l, this->maximum()); 0476 const int maxValue = 0477 qBound(this->minimum(), u, this->maximum()); 0478 const int midValue = 0479 qBound(this->minimum(), m, this->maximum()); 0480 0481 bool emitMinValChanged = (minValue != d->m_MinimumValue); 0482 bool emitMaxValChanged = (maxValue != d->m_MaximumValue); 0483 bool emitMidValChanged = (midValue != d->m_MidValue); 0484 0485 d->m_MinimumValue = minValue; 0486 d->m_MaximumValue = maxValue; 0487 d->m_MidValue = midValue; 0488 0489 bool emitMinPosChanged = 0490 (minValue != d->m_MinimumPosition); 0491 bool emitMaxPosChanged = 0492 (maxValue != d->m_MaximumPosition); 0493 bool emitMidPosChanged = 0494 (midValue != d->m_MidPosition); 0495 d->m_MinimumPosition = minValue; 0496 d->m_MaximumPosition = maxValue; 0497 d->m_MidPosition = midValue; 0498 0499 if (isSliderDown()) 0500 { 0501 if (emitMinPosChanged || emitMaxPosChanged || emitMidPosChanged) 0502 { 0503 emit positionsChanged(d->m_MinimumPosition, d->m_MidPosition, d->m_MaximumPosition); 0504 } 0505 if (emitMinPosChanged) 0506 { 0507 emit minimumPositionChanged(d->m_MinimumPosition); 0508 } 0509 if (emitMaxPosChanged) 0510 { 0511 emit maximumPositionChanged(d->m_MaximumPosition); 0512 } 0513 if (emitMidPosChanged) 0514 { 0515 emit midPositionChanged(d->m_MidPosition); 0516 } 0517 } 0518 if (emitMinValChanged || emitMaxValChanged || emitMidValChanged) 0519 { 0520 emit valuesChanged(d->m_MinimumValue, d->m_MidValue, d->m_MaximumValue); 0521 } 0522 if (emitMinValChanged) 0523 { 0524 emit minimumValueChanged(d->m_MinimumValue); 0525 } 0526 if (emitMaxValChanged) 0527 { 0528 emit maximumValueChanged(d->m_MaximumValue); 0529 } 0530 if (emitMidValChanged) 0531 { 0532 emit midValueChanged(d->m_MidValue); 0533 } 0534 if (emitMinPosChanged || emitMaxPosChanged || emitMidPosChanged || 0535 emitMinValChanged || emitMaxValChanged || emitMidValChanged) 0536 { 0537 this->update(); 0538 } 0539 } 0540 0541 // -------------------------------------------------------------------------- 0542 int ctk3Slider::minimumPosition() const 0543 { 0544 Q_D(const ctk3Slider); 0545 return d->m_MinimumPosition; 0546 } 0547 0548 // -------------------------------------------------------------------------- 0549 int ctk3Slider::maximumPosition() const 0550 { 0551 Q_D(const ctk3Slider); 0552 return d->m_MaximumPosition; 0553 } 0554 // -------------------------------------------------------------------------- 0555 int ctk3Slider::midPosition() const 0556 { 0557 Q_D(const ctk3Slider); 0558 return d->m_MidPosition; 0559 } 0560 0561 // -------------------------------------------------------------------------- 0562 void ctk3Slider::setMinimumPosition(int l) 0563 { 0564 Q_D(const ctk3Slider); 0565 // Min cannot be set higher than mid or max 0566 int newMin = qMin( qMin(l, d->m_MidPosition), d->m_MaximumPosition); 0567 this->setPositions(newMin, d->m_MidPosition, d->m_MaximumPosition); 0568 } 0569 0570 // -------------------------------------------------------------------------- 0571 void ctk3Slider::setMaximumPosition(int u) 0572 { 0573 Q_D(const ctk3Slider); 0574 // Max cannot be set lower than min or mid 0575 int newMax = qMax( qMax(u, d->m_MidPosition), d->m_MinimumPosition); 0576 this->setPositions(d->m_MinimumPosition, d->m_MidPosition, newMax); 0577 } 0578 // -------------------------------------------------------------------------- 0579 void ctk3Slider::setMidPosition(int m) 0580 { 0581 Q_D(const ctk3Slider); 0582 // Mid cannot be set lower than min or higher than max; 0583 int newMid = qMax(d->m_MinimumPosition, m); 0584 newMid = qMin(newMid, d->m_MaximumPosition); 0585 this->setPositions(d->m_MinimumPosition, newMid, d->m_MaximumPosition); 0586 } 0587 0588 // -------------------------------------------------------------------------- 0589 void ctk3Slider::setPositions(int min, int mid, int max) 0590 { 0591 Q_D(ctk3Slider); 0592 sortLMU(min, mid, max); 0593 const int minPosition = 0594 qBound(this->minimum(), min, this->maximum()); 0595 const int maxPosition = 0596 qBound(this->minimum(), max, this->maximum()); 0597 const int midPosition = 0598 qBound(this->minimum(), mid, this->maximum()); 0599 0600 bool emitMinPosChanged = (minPosition != d->m_MinimumPosition); 0601 bool emitMaxPosChanged = (maxPosition != d->m_MaximumPosition); 0602 bool emitMidPosChanged = (midPosition != d->m_MidPosition); 0603 0604 if (!emitMinPosChanged && !emitMaxPosChanged && !emitMidPosChanged) 0605 { 0606 return; 0607 } 0608 0609 d->m_MinimumPosition = minPosition; 0610 d->m_MaximumPosition = maxPosition; 0611 d->m_MidPosition = midPosition; 0612 0613 if (!this->hasTracking()) 0614 { 0615 this->update(); 0616 } 0617 if (isSliderDown()) 0618 { 0619 if (emitMinPosChanged) 0620 { 0621 emit minimumPositionChanged(d->m_MinimumPosition); 0622 } 0623 if (emitMaxPosChanged) 0624 { 0625 emit maximumPositionChanged(d->m_MaximumPosition); 0626 } 0627 if (emitMidPosChanged) 0628 { 0629 emit midPositionChanged(d->m_MidPosition); 0630 } 0631 if (emitMinPosChanged || emitMaxPosChanged || emitMidPosChanged) 0632 { 0633 emit positionsChanged(d->m_MinimumPosition, d->m_MidPosition, d->m_MaximumPosition); 0634 } 0635 } 0636 if (this->hasTracking()) 0637 { 0638 this->triggerAction(SliderMove); 0639 this->setValues(d->m_MinimumPosition, d->m_MidPosition, d->m_MaximumPosition); 0640 } 0641 } 0642 0643 // -------------------------------------------------------------------------- 0644 void ctk3Slider::onRangeChanged(int _minimum, int _maximum) 0645 { 0646 Q_UNUSED(_minimum); 0647 Q_UNUSED(_maximum); 0648 Q_D(ctk3Slider); 0649 this->setValues(d->m_MinimumValue, d->m_MidValue, d->m_MaximumValue); 0650 } 0651 0652 // -------------------------------------------------------------------------- 0653 // Render 0654 void ctk3Slider::paintEvent( QPaintEvent* ) 0655 { 0656 Q_D(ctk3Slider); 0657 QStyleOptionSlider option; 0658 this->initStyleOption(&option); 0659 0660 QStylePainter painter(this); 0661 option.subControls = QStyle::SC_SliderGroove; 0662 // Move to minimum to not highlight the SliderGroove. 0663 // On mac style, drawing just the slider groove also draws the handles, 0664 // therefore we give a negative (outside of view) position. 0665 option.sliderValue = this->minimum() - this->maximum(); 0666 option.sliderPosition = this->minimum() - this->maximum(); 0667 painter.drawComplexControl(QStyle::CC_Slider, option); 0668 0669 option.sliderPosition = d->m_MinimumPosition; 0670 const QRect lr = style()->subControlRect( QStyle::CC_Slider, 0671 &option, 0672 QStyle::SC_SliderHandle, 0673 this); 0674 0675 option.sliderPosition = d->m_MidPosition; 0676 0677 const QRect mr = style()->subControlRect( QStyle::CC_Slider, 0678 &option, 0679 QStyle::SC_SliderHandle, 0680 this); 0681 0682 option.sliderPosition = d->m_MaximumPosition; 0683 0684 const QRect ur = style()->subControlRect( QStyle::CC_Slider, 0685 &option, 0686 QStyle::SC_SliderHandle, 0687 this); 0688 0689 QRect sr = style()->subControlRect( QStyle::CC_Slider, 0690 &option, 0691 QStyle::SC_SliderGroove, 0692 this); 0693 QRect rangeBox; 0694 if (option.orientation == Qt::Horizontal) 0695 { 0696 rangeBox = QRect( 0697 QPoint(qMin( lr.center().x(), ur.center().x() ), sr.center().y() - 2), 0698 QPoint(qMax( lr.center().x(), ur.center().x() ), sr.center().y() + 1)); 0699 } 0700 else 0701 { 0702 rangeBox = QRect( 0703 QPoint(sr.center().x() - 2, qMin( lr.center().y(), ur.center().y() )), 0704 QPoint(sr.center().x() + 1, qMax( lr.center().y(), ur.center().y() ))); 0705 } 0706 0707 // ----------------------------- 0708 // Render the range 0709 // 0710 QRect groove = this->style()->subControlRect( QStyle::CC_Slider, 0711 &option, 0712 QStyle::SC_SliderGroove, 0713 this ); 0714 groove.adjust(0, 0, -1, 0); 0715 0716 // Create default colors based on the transfer function. 0717 // 0718 QColor highlight = this->palette().color(QPalette::Normal, QPalette::Highlight); 0719 QLinearGradient gradient; 0720 if (option.orientation == Qt::Horizontal) 0721 { 0722 gradient = QLinearGradient( groove.center().x(), groove.top(), 0723 groove.center().x(), groove.bottom()); 0724 } 0725 else 0726 { 0727 gradient = QLinearGradient( groove.left(), groove.center().y(), 0728 groove.right(), groove.center().y()); 0729 } 0730 0731 gradient.setColorAt(0, highlight.darker(120)); 0732 gradient.setColorAt(1, highlight.lighter(160)); 0733 0734 painter.setPen(QPen(highlight.darker(150), 0)); 0735 painter.setBrush(gradient); 0736 painter.drawRect( rangeBox.intersected(groove) ); 0737 0738 // ----------------------------------- 0739 // Render the sliders 0740 // 0741 if (this->isMinimumSliderDown()) 0742 { 0743 d->drawMaximumSlider( &painter ); 0744 d->drawMidSlider( &painter ); 0745 d->drawMinimumSlider( &painter ); 0746 } 0747 else if (this->isMidSliderDown()) 0748 { 0749 d->drawMaximumSlider( &painter ); 0750 d->drawMinimumSlider( &painter ); 0751 d->drawMidSlider( &painter ); 0752 } 0753 else 0754 { 0755 d->drawMinimumSlider( &painter ); 0756 d->drawMidSlider( &painter ); 0757 d->drawMaximumSlider( &painter ); 0758 } 0759 } 0760 0761 // -------------------------------------------------------------------------- 0762 // Standard Qt UI events 0763 void ctk3Slider::mousePressEvent(QMouseEvent* mouseEvent) 0764 { 0765 Q_D(ctk3Slider); 0766 if (minimum() == maximum() || (mouseEvent->buttons() ^ mouseEvent->button())) 0767 { 0768 mouseEvent->ignore(); 0769 return; 0770 } 0771 int mepos = this->orientation() == Qt::Horizontal ? 0772 mouseEvent->pos().x() : mouseEvent->pos().y(); 0773 0774 QStyleOptionSlider option; 0775 this->initStyleOption( &option ); 0776 0777 QRect handleRect; 0778 ctk3SliderPrivate::Handle handle_ = d->handleAtPos(mouseEvent->pos(), handleRect); 0779 0780 if (handle_ != ctk3SliderPrivate::NoHandle) 0781 { 0782 if (handle_ == ctk3SliderPrivate::MinimumHandle) 0783 d->m_SubclassPosition = d->m_MinimumPosition; 0784 else if (handle_ == ctk3SliderPrivate::MaximumHandle) 0785 d->m_SubclassPosition = d->m_MaximumPosition; 0786 else if (handle_ == ctk3SliderPrivate::MidHandle) 0787 d->m_SubclassPosition = d->m_MidPosition; 0788 0789 // save the position of the mouse inside the handle for later 0790 d->m_SubclassClickOffset = mepos - (this->orientation() == Qt::Horizontal ? 0791 handleRect.left() : handleRect.top()); 0792 0793 this->setSliderDown(true); 0794 0795 if (d->m_SelectedHandles != handle_) 0796 { 0797 d->m_SelectedHandles = handle_; 0798 this->update(handleRect); 0799 } 0800 // Accept the mouseEvent 0801 mouseEvent->accept(); 0802 return; 0803 } 0804 0805 // if we are here, no handles have been pressed 0806 // Check if we pressed on the groove between the 2 handles 0807 0808 QStyle::SubControl control = this->style()->hitTestComplexControl( 0809 QStyle::CC_Slider, &option, mouseEvent->pos(), this); 0810 QRect sr = style()->subControlRect( 0811 QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this); 0812 int minCenter = (this->orientation() == Qt::Horizontal ? 0813 handleRect.left() : handleRect.top()); 0814 int maxCenter = (this->orientation() == Qt::Horizontal ? 0815 handleRect.right() : handleRect.bottom()); 0816 if (control == QStyle::SC_SliderGroove && 0817 mepos > minCenter && mepos < maxCenter) 0818 { 0819 // warning lost of precision it might be fatal 0820 d->m_SubclassPosition = (d->m_MinimumPosition + d->m_MaximumPosition) / 2.; 0821 d->m_SubclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_SubclassPosition); 0822 d->m_SubclassWidth = (d->m_MaximumPosition - d->m_MinimumPosition) / 2; 0823 qMax(d->m_SubclassPosition - d->m_MinimumPosition, d->m_MaximumPosition - d->m_SubclassPosition); 0824 this->setSliderDown(true); 0825 if (!this->isMinimumSliderDown() || !this->isMaximumSliderDown()) 0826 { 0827 d->m_SelectedHandles = 0828 QFlags<ctk3SliderPrivate::Handle>(ctk3SliderPrivate::MinimumHandle) | 0829 QFlags<ctk3SliderPrivate::Handle>(ctk3SliderPrivate::MaximumHandle); 0830 this->update(handleRect.united(sr)); 0831 } 0832 mouseEvent->accept(); 0833 return; 0834 } 0835 mouseEvent->ignore(); 0836 } 0837 0838 // -------------------------------------------------------------------------- 0839 // Standard Qt UI events 0840 void ctk3Slider::mouseMoveEvent(QMouseEvent* mouseEvent) 0841 { 0842 Q_D(ctk3Slider); 0843 if (!d->m_SelectedHandles) 0844 { 0845 mouseEvent->ignore(); 0846 return; 0847 } 0848 int mepos = this->orientation() == Qt::Horizontal ? 0849 mouseEvent->pos().x() : mouseEvent->pos().y(); 0850 0851 QStyleOptionSlider option; 0852 this->initStyleOption(&option); 0853 0854 const int m = style()->pixelMetric( QStyle::PM_MaximumDragDistance, &option, this ); 0855 0856 int newPosition = d->pixelPosToRangeValue(mepos - d->m_SubclassClickOffset); 0857 0858 if (m >= 0) 0859 { 0860 const QRect r = rect().adjusted(-m, -m, m, m); 0861 if (!r.contains(mouseEvent->pos())) 0862 { 0863 newPosition = d->m_SubclassPosition; 0864 } 0865 } 0866 0867 // If two sliders down, pick the one that makes sense. 0868 // TODO: this won't trigger because right not the code doesn't recognize two sliders down. 0869 if (this->isMinimumSliderDown() && this->isMidSliderDown()) 0870 { 0871 // To break the tie, first try min, but only if the new position 0872 // isn't > the mid position 0873 if (newPosition < d->m_MidPosition) 0874 this->setPositions(newPosition, d->m_MidPosition, d->m_MaximumPosition); 0875 else 0876 this->setPositions(d->m_MinimumPosition, newPosition, d->m_MaximumPosition); 0877 } 0878 else if (this->isMidSliderDown() && this->isMaximumSliderDown()) 0879 { 0880 // To break the tie, first try min, but only if the new position 0881 // isn't > the mid position 0882 if (newPosition < d->m_MaximumPosition) 0883 this->setPositions(d->m_MinimumPosition, newPosition, d->m_MaximumPosition); 0884 else 0885 this->setPositions(d->m_MinimumPosition, d->m_MidPosition, newPosition); 0886 } 0887 else if (this->isMinimumSliderDown()) 0888 { 0889 double newMinPos = qMin(newPosition, d->m_MidPosition); 0890 this->setPositions(newMinPos, d->m_MidPosition, d->m_MaximumPosition); 0891 } 0892 // The upper/right slider is down 0893 else if (this->isMaximumSliderDown()) 0894 { 0895 double newMaxPos = qMax(d->m_MidPosition, newPosition); 0896 this->setPositions(d->m_MinimumPosition, d->m_MidPosition, newMaxPos); 0897 } 0898 // The mid slider is down 0899 else if (this->isMidSliderDown()) 0900 { 0901 double newMidPos = qMin(qMax(d->m_MinimumPosition, newPosition), d->m_MaximumPosition); 0902 this->setPositions(d->m_MinimumPosition, newMidPos, d->m_MaximumPosition); 0903 } 0904 mouseEvent->accept(); 0905 } 0906 0907 // -------------------------------------------------------------------------- 0908 // Standard Qt UI mouseEvents 0909 void ctk3Slider::mouseReleaseEvent(QMouseEvent* mouseEvent) 0910 { 0911 Q_D(ctk3Slider); 0912 this->QSlider::mouseReleaseEvent(mouseEvent); 0913 0914 setSliderDown(false); 0915 d->m_SelectedHandles = ctk3SliderPrivate::NoHandle; 0916 0917 emit released(d->m_MinimumValue, d->m_MidValue, d->m_MaximumValue); 0918 this->update(); 0919 } 0920 0921 // -------------------------------------------------------------------------- 0922 bool ctk3Slider::isMinimumSliderDown()const 0923 { 0924 Q_D(const ctk3Slider); 0925 return d->m_SelectedHandles & ctk3SliderPrivate::MinimumHandle; 0926 } 0927 0928 // -------------------------------------------------------------------------- 0929 bool ctk3Slider::isMaximumSliderDown()const 0930 { 0931 Q_D(const ctk3Slider); 0932 return d->m_SelectedHandles & ctk3SliderPrivate::MaximumHandle; 0933 } 0934 // -------------------------------------------------------------------------- 0935 bool ctk3Slider::isMidSliderDown()const 0936 { 0937 Q_D(const ctk3Slider); 0938 return d->m_SelectedHandles & ctk3SliderPrivate::MidHandle; 0939 } 0940 0941 // -------------------------------------------------------------------------- 0942 void ctk3Slider::initMinimumSliderStyleOption(QStyleOptionSlider* option) const 0943 { 0944 this->initStyleOption(option); 0945 } 0946 0947 // -------------------------------------------------------------------------- 0948 void ctk3Slider::initMaximumSliderStyleOption(QStyleOptionSlider* option) const 0949 { 0950 this->initStyleOption(option); 0951 } 0952 // -------------------------------------------------------------------------- 0953 void ctk3Slider::initMidSliderStyleOption(QStyleOptionSlider* option) const 0954 { 0955 this->initStyleOption(option); 0956 } 0957 0958 // -------------------------------------------------------------------------- 0959 QString ctk3Slider::handleToolTip()const 0960 { 0961 Q_D(const ctk3Slider); 0962 return d->m_HandleToolTip; 0963 } 0964 0965 // -------------------------------------------------------------------------- 0966 void ctk3Slider::setHandleToolTip(const QString &_toolTip) 0967 { 0968 Q_D(ctk3Slider); 0969 d->m_HandleToolTip = _toolTip; 0970 } 0971 0972 // -------------------------------------------------------------------------- 0973 bool ctk3Slider::event(QEvent* _event) 0974 { 0975 Q_D(ctk3Slider); 0976 switch(_event->type()) 0977 { 0978 case QEvent::ToolTip: 0979 { 0980 QHelpEvent* helpEvent = static_cast<QHelpEvent*>(_event); 0981 QStyleOptionSlider opt; 0982 // Test the MinimumHandle 0983 opt.sliderPosition = d->m_MinimumPosition; 0984 opt.sliderValue = d->m_MinimumValue; 0985 this->initStyleOption(&opt); 0986 QStyle::SubControl hoveredControl = 0987 this->style()->hitTestComplexControl( 0988 QStyle::CC_Slider, &opt, helpEvent->pos(), this); 0989 if (!d->m_HandleToolTip.isEmpty() && 0990 hoveredControl == QStyle::SC_SliderHandle) 0991 { 0992 QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->minimumValue())); 0993 _event->accept(); 0994 return true; 0995 } 0996 // Test the MaximumHandle 0997 opt.sliderPosition = d->m_MaximumPosition; 0998 opt.sliderValue = d->m_MaximumValue; 0999 this->initStyleOption(&opt); 1000 hoveredControl = this->style()->hitTestComplexControl( 1001 QStyle::CC_Slider, &opt, helpEvent->pos(), this); 1002 if (!d->m_HandleToolTip.isEmpty() && 1003 hoveredControl == QStyle::SC_SliderHandle) 1004 { 1005 QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->maximumValue())); 1006 _event->accept(); 1007 return true; 1008 } 1009 // Test the MidHandle 1010 opt.sliderPosition = d->m_MidPosition; 1011 opt.sliderValue = d->m_MidValue; 1012 this->initStyleOption(&opt); 1013 hoveredControl = this->style()->hitTestComplexControl( 1014 QStyle::CC_Slider, &opt, helpEvent->pos(), this); 1015 if (!d->m_HandleToolTip.isEmpty() && 1016 hoveredControl == QStyle::SC_SliderHandle) 1017 { 1018 QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->midValue())); 1019 _event->accept(); 1020 return true; 1021 } 1022 } 1023 default: 1024 break; 1025 } 1026 return this->Superclass::event(_event); 1027 }