Kstars

ctk3slider.cpp
1/*
2 SPDX-FileCopyrightText: Kitware Inc.
3 SPDX-License-Identifier: Apache-2.0
4
5 Modified from the original code to support 3 sliders, May 2023.
6*/
7
8// Qt includes
9#include <QDebug>
10#include <QMouseEvent>
11#include <QKeyEvent>
12#include <QStyleOptionSlider>
13#include <QApplication>
14#include <QStylePainter>
15#include <QStyle>
16#include <QToolTip>
17
18#include "ctk3slider.h"
19
20class ctk3SliderPrivate
21{
22 Q_DECLARE_PUBLIC(ctk3Slider)
23 protected:
24 ctk3Slider* const q_ptr;
25 public:
26 /// Boolean indicates the selected handle
27 /// True for the minimum range handle, false for the maximum range handle
28 enum Handle
29 {
30 NoHandle = 0x0,
31 MinimumHandle = 0x1,
32 MidHandle = 0x2,
33 MaximumHandle = 0x4
34 };
35 Q_DECLARE_FLAGS(Handles, Handle)
36
37 ctk3SliderPrivate(ctk3Slider &object);
38 void init();
39
40 /// Return the handle at the given pos, or none if no handle is at the pos.
41 /// If a handle is selected, handleRect is set to the handle rect.
42 /// otherwise return NoHandle and handleRect is set to the combined rect of
43 /// the min and max handles
44 Handle handleAtPos(const QPoint &pos, QRect &handleRect)const;
45
46 /// Copied verbatim from QSliderPrivate class (see QSlider.cpp)
47 int pixelPosToRangeValue(int pos) const;
48 int pixelPosFromRangeValue(int val) const;
49
50 /// Draw the bottom and top sliders.
51 void drawMinimumSlider( QStylePainter* painter ) const;
52 void drawMaximumSlider( QStylePainter* painter ) const;
53 void drawMidSlider( QStylePainter* painter ) const;
54
55 /// End points of the range on the Model
56 int m_MaximumValue;
57 int m_MinimumValue;
58 int m_MidValue;
59
60 /// End points of the range on the GUI. This is synced with the model.
61 int m_MaximumPosition;
62 int m_MinimumPosition;
63 int m_MidPosition;
64
65 /// Controls selected ?
66 QStyle::SubControl m_MinimumSliderSelected;
67 QStyle::SubControl m_MaximumSliderSelected;
68 QStyle::SubControl m_MidSliderSelected;
69
70 /// See QSliderPrivate::clickOffset.
71 /// Overrides this ivar
72 int m_SubclassClickOffset;
73
74 /// See QSliderPrivate::position
75 /// Overrides this ivar.
76 int m_SubclassPosition;
77
78 /// Original width between the 2 bounds before any moves
79 int m_SubclassWidth;
80
81 ctk3SliderPrivate::Handles m_SelectedHandles;
82
83 QString m_HandleToolTip;
84
85 private:
86 Q_DISABLE_COPY(ctk3SliderPrivate)
87};
88
89// --------------------------------------------------------------------------
90ctk3SliderPrivate::ctk3SliderPrivate(ctk3Slider &object)
91 : q_ptr(&object)
92{
93 this->m_MinimumValue = 0;
94 this->m_MaximumValue = 100;
95 this->m_MidValue = 50;
96 this->m_MinimumPosition = 0;
97 this->m_MaximumPosition = 100;
98 this->m_MidPosition = 50;
99 this->m_MinimumSliderSelected = QStyle::SC_None;
100 this->m_MaximumSliderSelected = QStyle::SC_None;
101 this->m_MidSliderSelected = QStyle::SC_None;
102 this->m_SubclassClickOffset = 0;
103 this->m_SubclassPosition = 0;
104 this->m_SubclassWidth = 0;
105 this->m_SelectedHandles = ctk3SliderPrivate::NoHandle;
106}
107
108// --------------------------------------------------------------------------
109void ctk3SliderPrivate::init()
110{
111 Q_Q(ctk3Slider);
112 this->m_MinimumValue = q->minimum();
113 this->m_MaximumValue = q->maximum();
114 this->m_MaximumValue = (q->minimum() + q->maximum()) / 2.0;
115 this->m_MinimumPosition = q->minimum();
116 this->m_MaximumPosition = q->maximum();
117 this->m_MidPosition = (q->minimum() + q->maximum()) / 2.0;
118 q->connect(q, SIGNAL(rangeChanged(int, int)), q, SLOT(onRangeChanged(int, int)));
119}
120
121// --------------------------------------------------------------------------
122ctk3SliderPrivate::Handle ctk3SliderPrivate::handleAtPos(const QPoint &pos, QRect &handleRect)const
123{
124 Q_Q(const ctk3Slider);
125
126 QStyleOptionSlider option;
127 q->initStyleOption( &option );
128
129 // The functions hitTestComplexControl only know about 1 handle. As we have
130 // 3, we change the position of the handle and test if the pos correspond to
131 // any of the 3 positions.
132
133 // Test the MinimumHandle
134 option.sliderPosition = this->m_MinimumPosition;
135 option.sliderValue = this->m_MinimumValue;
136
137 QStyle::SubControl minimumControl = q->style()->hitTestComplexControl(
138 QStyle::CC_Slider, &option, pos, q);
139 QRect minimumHandleRect = q->style()->subControlRect(
141
142 // Test if the pos is under the Maximum handle
143 option.sliderPosition = this->m_MaximumPosition;
144 option.sliderValue = this->m_MaximumValue;
145
146 QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(
147 QStyle::CC_Slider, &option, pos, q);
148 QRect maximumHandleRect = q->style()->subControlRect(
150
151 // Test if the pos is under the Mid handle
152 option.sliderPosition = this->m_MidPosition;
153 option.sliderValue = this->m_MidValue;
154
155 QStyle::SubControl midControl = q->style()->hitTestComplexControl(
156 QStyle::CC_Slider, &option, pos, q);
157 QRect midHandleRect = q->style()->subControlRect(
159
160 if (minimumControl == QStyle::SC_SliderHandle)
161 {
162 handleRect = minimumHandleRect;
163 return MinimumHandle;
164 }
165 else if (midControl == QStyle::SC_SliderHandle)
166 {
167 handleRect = midHandleRect;
168 return MidHandle;
169 }
170 else if (maximumControl == QStyle::SC_SliderHandle)
171 {
172 handleRect = maximumHandleRect;
173 return MaximumHandle;
174 }
175 handleRect = minimumHandleRect.united(maximumHandleRect);
176 return NoHandle;
177}
178
179// --------------------------------------------------------------------------
180// Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp
181//
182int ctk3SliderPrivate::pixelPosToRangeValue( int pos ) const
183{
184 Q_Q(const ctk3Slider);
185 QStyleOptionSlider option;
186 q->initStyleOption( &option );
187
188 QRect gr = q->style()->subControlRect( QStyle::CC_Slider,
189 &option,
191 q );
192 QRect sr = q->style()->subControlRect( QStyle::CC_Slider,
193 &option,
195 q );
196 int sliderMin, sliderMax, sliderLength;
197 if (option.orientation == Qt::Horizontal)
198 {
199 sliderLength = sr.width();
200 sliderMin = gr.x();
201 sliderMax = gr.right() - sliderLength + 1;
202 }
203 else
204 {
205 sliderLength = sr.height();
206 sliderMin = gr.y();
207 sliderMax = gr.bottom() - sliderLength + 1;
208 }
209
210 return QStyle::sliderValueFromPosition( q->minimum(),
211 q->maximum(),
212 pos - sliderMin,
213 sliderMax - sliderMin,
214 option.upsideDown );
215}
216
217//---------------------------------------------------------------------------
218int ctk3SliderPrivate::pixelPosFromRangeValue( int val ) const
219{
220 Q_Q(const ctk3Slider);
221 QStyleOptionSlider option;
222 q->initStyleOption( &option );
223
224 QRect gr = q->style()->subControlRect( QStyle::CC_Slider,
225 &option,
227 q );
228 QRect sr = q->style()->subControlRect( QStyle::CC_Slider,
229 &option,
231 q );
232 int sliderMin, sliderMax, sliderLength;
233 if (option.orientation == Qt::Horizontal)
234 {
235 sliderLength = sr.width();
236 sliderMin = gr.x();
237 sliderMax = gr.right() - sliderLength + 1;
238 }
239 else
240 {
241 sliderLength = sr.height();
242 sliderMin = gr.y();
243 sliderMax = gr.bottom() - sliderLength + 1;
244 }
245
246 return QStyle::sliderPositionFromValue( q->minimum(),
247 q->maximum(),
248 val,
249 sliderMax - sliderMin,
250 option.upsideDown ) + sliderMin;
251}
252
253//---------------------------------------------------------------------------
254// Draw slider at the bottom end of the range
255void ctk3SliderPrivate::drawMinimumSlider( QStylePainter* painter ) const
256{
257 Q_Q(const ctk3Slider);
258 QStyleOptionSlider option;
259 q->initMinimumSliderStyleOption( &option );
260
261 option.subControls = QStyle::SC_SliderHandle;
262 option.sliderValue = m_MinimumValue;
263 option.sliderPosition = m_MinimumPosition;
264 if (q->isMinimumSliderDown())
265 {
266 option.activeSubControls = QStyle::SC_SliderHandle;
267 option.state |= QStyle::State_Sunken;
268 }
269#ifdef Q_OS_MAC
270 // On mac style, drawing just the handle actually draws also the groove.
271 QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
273 painter->setClipRect(clip);
274#endif
275 painter->drawComplexControl(QStyle::CC_Slider, option);
276}
277
278//---------------------------------------------------------------------------
279// Draw slider at the top end of the range
280void ctk3SliderPrivate::drawMaximumSlider( QStylePainter* painter ) const
281{
282 Q_Q(const ctk3Slider);
283 QStyleOptionSlider option;
284 q->initMaximumSliderStyleOption( &option );
285
286 option.subControls = QStyle::SC_SliderHandle;
287 option.sliderValue = m_MaximumValue;
288 option.sliderPosition = m_MaximumPosition;
289 if (q->isMaximumSliderDown())
290 {
291 option.activeSubControls = QStyle::SC_SliderHandle;
292 option.state |= QStyle::State_Sunken;
293 }
294#ifdef Q_OS_MAC
295 // On mac style, drawing just the handle actually draws also the groove.
296 QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
298 painter->setClipRect(clip);
299#endif
300 painter->drawComplexControl(QStyle::CC_Slider, option);
301}
302
303// Draw the mid slider
304void ctk3SliderPrivate::drawMidSlider( QStylePainter* painter ) const
305{
306 Q_Q(const ctk3Slider);
307 QStyleOptionSlider option;
308 q->initMidSliderStyleOption( &option );
309
310 option.subControls = QStyle::SC_SliderHandle;
311 option.sliderValue = m_MidValue;
312 option.sliderPosition = m_MidPosition;
313 if (q->isMidSliderDown())
314 {
315 option.activeSubControls = QStyle::SC_SliderHandle;
316 option.state |= QStyle::State_Sunken;
317 }
318#ifdef Q_OS_MAC
319 // On mac style, drawing just the handle actually draws also the groove.
320 QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
322 painter->setClipRect(clip);
323#endif
324 painter->drawComplexControl(QStyle::CC_Slider, option);
325}
326
327// --------------------------------------------------------------------------
329 : QSlider(_parent)
330 , d_ptr(new ctk3SliderPrivate(*this))
331{
333 d->init();
334}
335
336// --------------------------------------------------------------------------
338 QWidget* parentObject )
339 : QSlider(o, parentObject)
340 , d_ptr(new ctk3SliderPrivate(*this))
341{
343 d->init();
344}
345
346// --------------------------------------------------------------------------
347ctk3Slider::ctk3Slider(ctk3SliderPrivate* impl, QWidget* _parent)
348 : QSlider(_parent)
349 , d_ptr(impl)
350{
352 d->init();
353}
354
355// --------------------------------------------------------------------------
356ctk3Slider::ctk3Slider( ctk3SliderPrivate* impl, Qt::Orientation o,
357 QWidget* parentObject )
358 : QSlider(o, parentObject)
359 , d_ptr(impl)
360{
362 d->init();
363}
364
365// --------------------------------------------------------------------------
366ctk3Slider::~ctk3Slider()
367{
368}
369
370// --------------------------------------------------------------------------
371int ctk3Slider::minimumValue() const
372{
373 Q_D(const ctk3Slider);
374 return d->m_MinimumValue;
375}
376
377// --------------------------------------------------------------------------
379{
381 // Min cannot be set higher than mid or max
382 int newMin = qMin( qMin(min, d->m_MidValue), d->m_MaximumValue);
383 this->setValues( newMin, d->m_MidValue, d->m_MaximumValue);
384}
385
386// --------------------------------------------------------------------------
387int ctk3Slider::maximumValue() const
388{
389 Q_D(const ctk3Slider);
390 return d->m_MaximumValue;
391}
392
393// --------------------------------------------------------------------------
395{
397 // Max cannot be set lower than min or mid
398 int newMax = qMax( qMax(max, d->m_MidValue), d->m_MinimumValue);
399 this->setValues(d->m_MinimumValue, d->m_MidValue, newMax);
400}
401
402// --------------------------------------------------------------------------
403int ctk3Slider::midValue() const
404{
405 Q_D(const ctk3Slider);
406 return d->m_MidValue;
407}
408
409// --------------------------------------------------------------------------
411{
413 // Mid cannot be set lower than min or higher than max;
414 int newMid = qMax(d->m_MinimumValue, mid);
415 newMid = qMin(newMid, d->m_MaximumValue);
416 this->setValues(d->m_MinimumValue, newMid, d->m_MaximumValue);
417}
418
419// --------------------------------------------------------------------------
420namespace
421{
422// Sorts the 3 numbers so l becomes the least, and u becomes the largest.
423void sortLMU(int &l, int &m, int &u)
424{
425 int kL = l, kM = m, kU = u;
426 if (l <= m && l <= u) // l is the smallest
427 {
428 if (m <= u) // order is l,m,u
429 return;
430 else // order is l, u, m
431 {
432 l = kL;
433 m = kU;
434 u = kM;
435 }
436 }
437 else if (m <= l && m <= u) // m is the smallest
438 {
439 if (l <= u) // order is m, l, u
440 {
441 l = kM;
442 m = kL;
443 u = kU;
444 }
445 else // order is m, u, l
446 {
447 l = kM;
448 m = kU;
449 u = kL;
450 }
451 }
452 else // u is the smallest
453 {
454 if (l <= m) // order is u, l, m
455 {
456 l = kL;
457 m = kU;
458 u = kM;
459 l = m;
460 }
461 else // order is u, m, l
462 {
463 l = kU;
464 m = kM;
465 u = kL;
466 }
467 }
468}
469}
470void ctk3Slider::setValues(int l, int m, int u)
471{
473 sortLMU(l, m, u);
474 const int minValue =
475 qBound(this->minimum(), l, this->maximum());
476 const int maxValue =
477 qBound(this->minimum(), u, this->maximum());
478 const int midValue =
479 qBound(this->minimum(), m, this->maximum());
480
481 bool emitMinValChanged = (minValue != d->m_MinimumValue);
482 bool emitMaxValChanged = (maxValue != d->m_MaximumValue);
483 bool emitMidValChanged = (midValue != d->m_MidValue);
484
485 d->m_MinimumValue = minValue;
486 d->m_MaximumValue = maxValue;
487 d->m_MidValue = midValue;
488
489 bool emitMinPosChanged =
490 (minValue != d->m_MinimumPosition);
491 bool emitMaxPosChanged =
492 (maxValue != d->m_MaximumPosition);
493 bool emitMidPosChanged =
494 (midValue != d->m_MidPosition);
495 d->m_MinimumPosition = minValue;
496 d->m_MaximumPosition = maxValue;
497 d->m_MidPosition = midValue;
498
499 if (isSliderDown())
500 {
501 if (emitMinPosChanged || emitMaxPosChanged || emitMidPosChanged)
502 {
503 emit positionsChanged(d->m_MinimumPosition, d->m_MidPosition, d->m_MaximumPosition);
504 }
505 if (emitMinPosChanged)
506 {
507 emit minimumPositionChanged(d->m_MinimumPosition);
508 }
509 if (emitMaxPosChanged)
510 {
511 emit maximumPositionChanged(d->m_MaximumPosition);
512 }
513 if (emitMidPosChanged)
514 {
515 emit midPositionChanged(d->m_MidPosition);
516 }
517 }
518 if (emitMinValChanged || emitMaxValChanged || emitMidValChanged)
519 {
520 emit valuesChanged(d->m_MinimumValue, d->m_MidValue, d->m_MaximumValue);
521 }
522 if (emitMinValChanged)
523 {
524 emit minimumValueChanged(d->m_MinimumValue);
525 }
526 if (emitMaxValChanged)
527 {
528 emit maximumValueChanged(d->m_MaximumValue);
529 }
530 if (emitMidValChanged)
531 {
532 emit midValueChanged(d->m_MidValue);
533 }
534 if (emitMinPosChanged || emitMaxPosChanged || emitMidPosChanged ||
535 emitMinValChanged || emitMaxValChanged || emitMidValChanged)
536 {
537 this->update();
538 }
539}
540
541// --------------------------------------------------------------------------
542int ctk3Slider::minimumPosition() const
543{
544 Q_D(const ctk3Slider);
545 return d->m_MinimumPosition;
546}
547
548// --------------------------------------------------------------------------
549int ctk3Slider::maximumPosition() const
550{
551 Q_D(const ctk3Slider);
552 return d->m_MaximumPosition;
553}
554// --------------------------------------------------------------------------
555int ctk3Slider::midPosition() const
556{
557 Q_D(const ctk3Slider);
558 return d->m_MidPosition;
559}
560
561// --------------------------------------------------------------------------
562void ctk3Slider::setMinimumPosition(int l)
563{
564 Q_D(const ctk3Slider);
565 // Min cannot be set higher than mid or max
566 int newMin = qMin( qMin(l, d->m_MidPosition), d->m_MaximumPosition);
567 this->setPositions(newMin, d->m_MidPosition, d->m_MaximumPosition);
568}
569
570// --------------------------------------------------------------------------
571void ctk3Slider::setMaximumPosition(int u)
572{
573 Q_D(const ctk3Slider);
574 // Max cannot be set lower than min or mid
575 int newMax = qMax( qMax(u, d->m_MidPosition), d->m_MinimumPosition);
576 this->setPositions(d->m_MinimumPosition, d->m_MidPosition, newMax);
577}
578// --------------------------------------------------------------------------
579void ctk3Slider::setMidPosition(int m)
580{
581 Q_D(const ctk3Slider);
582 // Mid cannot be set lower than min or higher than max;
583 int newMid = qMax(d->m_MinimumPosition, m);
584 newMid = qMin(newMid, d->m_MaximumPosition);
585 this->setPositions(d->m_MinimumPosition, newMid, d->m_MaximumPosition);
586}
587
588// --------------------------------------------------------------------------
589void ctk3Slider::setPositions(int min, int mid, int max)
590{
592 sortLMU(min, mid, max);
593 const int minPosition =
594 qBound(this->minimum(), min, this->maximum());
595 const int maxPosition =
596 qBound(this->minimum(), max, this->maximum());
597 const int midPosition =
598 qBound(this->minimum(), mid, this->maximum());
599
600 bool emitMinPosChanged = (minPosition != d->m_MinimumPosition);
601 bool emitMaxPosChanged = (maxPosition != d->m_MaximumPosition);
602 bool emitMidPosChanged = (midPosition != d->m_MidPosition);
603
604 if (!emitMinPosChanged && !emitMaxPosChanged && !emitMidPosChanged)
605 {
606 return;
607 }
608
609 d->m_MinimumPosition = minPosition;
610 d->m_MaximumPosition = maxPosition;
611 d->m_MidPosition = midPosition;
612
613 if (!this->hasTracking())
614 {
615 this->update();
616 }
617 if (isSliderDown())
618 {
619 if (emitMinPosChanged)
620 {
621 emit minimumPositionChanged(d->m_MinimumPosition);
622 }
623 if (emitMaxPosChanged)
624 {
625 emit maximumPositionChanged(d->m_MaximumPosition);
626 }
627 if (emitMidPosChanged)
628 {
629 emit midPositionChanged(d->m_MidPosition);
630 }
631 if (emitMinPosChanged || emitMaxPosChanged || emitMidPosChanged)
632 {
633 emit positionsChanged(d->m_MinimumPosition, d->m_MidPosition, d->m_MaximumPosition);
634 }
635 }
636 if (this->hasTracking())
637 {
639 this->setValues(d->m_MinimumPosition, d->m_MidPosition, d->m_MaximumPosition);
640 }
641}
642
643// --------------------------------------------------------------------------
644void ctk3Slider::onRangeChanged(int _minimum, int _maximum)
645{
646 Q_UNUSED(_minimum);
647 Q_UNUSED(_maximum);
649 this->setValues(d->m_MinimumValue, d->m_MidValue, d->m_MaximumValue);
650}
651
652// --------------------------------------------------------------------------
653// Render
654void ctk3Slider::paintEvent( QPaintEvent* )
655{
657 QStyleOptionSlider option;
658 this->initStyleOption(&option);
659
660 QStylePainter painter(this);
661 option.subControls = QStyle::SC_SliderGroove;
662 // Move to minimum to not highlight the SliderGroove.
663 // On mac style, drawing just the slider groove also draws the handles,
664 // therefore we give a negative (outside of view) position.
665 option.sliderValue = this->minimum() - this->maximum();
666 option.sliderPosition = this->minimum() - this->maximum();
667 painter.drawComplexControl(QStyle::CC_Slider, option);
668
669 option.sliderPosition = d->m_MinimumPosition;
671 &option,
673 this);
674
675 option.sliderPosition = d->m_MidPosition;
676
678 &option,
680 this);
681
682 option.sliderPosition = d->m_MaximumPosition;
683
685 &option,
687 this);
688
690 &option,
692 this);
693 QRect rangeBox;
694 if (option.orientation == Qt::Horizontal)
695 {
696 rangeBox = QRect(
697 QPoint(qMin( lr.center().x(), ur.center().x() ), sr.center().y() - 2),
698 QPoint(qMax( lr.center().x(), ur.center().x() ), sr.center().y() + 1));
699 }
700 else
701 {
702 rangeBox = QRect(
703 QPoint(sr.center().x() - 2, qMin( lr.center().y(), ur.center().y() )),
704 QPoint(sr.center().x() + 1, qMax( lr.center().y(), ur.center().y() )));
705 }
706
707 // -----------------------------
708 // Render the range
709 //
710 QRect groove = this->style()->subControlRect( QStyle::CC_Slider,
711 &option,
713 this );
714 groove.adjust(0, 0, -1, 0);
715
716 // Create default colors based on the transfer function.
717 //
718 QColor highlight = this->palette().color(QPalette::Normal, QPalette::Highlight);
719 QLinearGradient gradient;
720 if (option.orientation == Qt::Horizontal)
721 {
722 gradient = QLinearGradient( groove.center().x(), groove.top(),
723 groove.center().x(), groove.bottom());
724 }
725 else
726 {
727 gradient = QLinearGradient( groove.left(), groove.center().y(),
728 groove.right(), groove.center().y());
729 }
730
731 gradient.setColorAt(0, highlight.darker(120));
732 gradient.setColorAt(1, highlight.lighter(160));
733
734 painter.setPen(QPen(highlight.darker(150), 0));
735 painter.setBrush(gradient);
736 painter.drawRect( rangeBox.intersected(groove) );
737
738 // -----------------------------------
739 // Render the sliders
740 //
741 if (this->isMinimumSliderDown())
742 {
743 d->drawMaximumSlider( &painter );
744 d->drawMidSlider( &painter );
745 d->drawMinimumSlider( &painter );
746 }
747 else if (this->isMidSliderDown())
748 {
749 d->drawMaximumSlider( &painter );
750 d->drawMinimumSlider( &painter );
751 d->drawMidSlider( &painter );
752 }
753 else
754 {
755 d->drawMinimumSlider( &painter );
756 d->drawMidSlider( &painter );
757 d->drawMaximumSlider( &painter );
758 }
759}
760
761// --------------------------------------------------------------------------
762// Standard Qt UI events
763void ctk3Slider::mousePressEvent(QMouseEvent* mouseEvent)
764{
766 if (minimum() == maximum() || (mouseEvent->buttons() ^ mouseEvent->button()))
767 {
768 mouseEvent->ignore();
769 return;
770 }
771 int mepos = this->orientation() == Qt::Horizontal ?
772 mouseEvent->pos().x() : mouseEvent->pos().y();
773
774 QStyleOptionSlider option;
775 this->initStyleOption( &option );
776
777 QRect handleRect;
778 ctk3SliderPrivate::Handle handle_ = d->handleAtPos(mouseEvent->pos(), handleRect);
779
780 if (handle_ != ctk3SliderPrivate::NoHandle)
781 {
782 if (handle_ == ctk3SliderPrivate::MinimumHandle)
783 d->m_SubclassPosition = d->m_MinimumPosition;
784 else if (handle_ == ctk3SliderPrivate::MaximumHandle)
785 d->m_SubclassPosition = d->m_MaximumPosition;
786 else if (handle_ == ctk3SliderPrivate::MidHandle)
787 d->m_SubclassPosition = d->m_MidPosition;
788
789 // save the position of the mouse inside the handle for later
790 d->m_SubclassClickOffset = mepos - (this->orientation() == Qt::Horizontal ?
791 handleRect.left() : handleRect.top());
792
793 this->setSliderDown(true);
794
795 if (d->m_SelectedHandles != handle_)
796 {
797 d->m_SelectedHandles = handle_;
798 this->update(handleRect);
799 }
800 // Accept the mouseEvent
801 mouseEvent->accept();
802 return;
803 }
804
805 // if we are here, no handles have been pressed
806 // Check if we pressed on the groove between the 2 handles
807
809 QStyle::CC_Slider, &option, mouseEvent->pos(), this);
810 QRect sr = style()->subControlRect(
812 int minCenter = (this->orientation() == Qt::Horizontal ?
813 handleRect.left() : handleRect.top());
814 int maxCenter = (this->orientation() == Qt::Horizontal ?
815 handleRect.right() : handleRect.bottom());
816 if (control == QStyle::SC_SliderGroove &&
817 mepos > minCenter && mepos < maxCenter)
818 {
819 // warning lost of precision it might be fatal
820 d->m_SubclassPosition = (d->m_MinimumPosition + d->m_MaximumPosition) / 2.;
821 d->m_SubclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_SubclassPosition);
822 d->m_SubclassWidth = (d->m_MaximumPosition - d->m_MinimumPosition) / 2;
823 qMax(d->m_SubclassPosition - d->m_MinimumPosition, d->m_MaximumPosition - d->m_SubclassPosition);
824 this->setSliderDown(true);
825 if (!this->isMinimumSliderDown() || !this->isMaximumSliderDown())
826 {
827 d->m_SelectedHandles =
828 QFlags<ctk3SliderPrivate::Handle>(ctk3SliderPrivate::MinimumHandle) |
829 QFlags<ctk3SliderPrivate::Handle>(ctk3SliderPrivate::MaximumHandle);
830 this->update(handleRect.united(sr));
831 }
832 mouseEvent->accept();
833 return;
834 }
835 mouseEvent->ignore();
836}
837
838// --------------------------------------------------------------------------
839// Standard Qt UI events
840void ctk3Slider::mouseMoveEvent(QMouseEvent* mouseEvent)
841{
843 if (!d->m_SelectedHandles)
844 {
845 mouseEvent->ignore();
846 return;
847 }
848 int mepos = this->orientation() == Qt::Horizontal ?
849 mouseEvent->pos().x() : mouseEvent->pos().y();
850
851 QStyleOptionSlider option;
852 this->initStyleOption(&option);
853
854 const int m = style()->pixelMetric( QStyle::PM_MaximumDragDistance, &option, this );
855
856 int newPosition = d->pixelPosToRangeValue(mepos - d->m_SubclassClickOffset);
857
858 if (m >= 0)
859 {
860 const QRect r = rect().adjusted(-m, -m, m, m);
861 if (!r.contains(mouseEvent->pos()))
862 {
863 newPosition = d->m_SubclassPosition;
864 }
865 }
866
867 // If two sliders down, pick the one that makes sense.
868 // TODO: this won't trigger because right not the code doesn't recognize two sliders down.
869 if (this->isMinimumSliderDown() && this->isMidSliderDown())
870 {
871 // To break the tie, first try min, but only if the new position
872 // isn't > the mid position
873 if (newPosition < d->m_MidPosition)
874 this->setPositions(newPosition, d->m_MidPosition, d->m_MaximumPosition);
875 else
876 this->setPositions(d->m_MinimumPosition, newPosition, d->m_MaximumPosition);
877 }
878 else if (this->isMidSliderDown() && this->isMaximumSliderDown())
879 {
880 // To break the tie, first try min, but only if the new position
881 // isn't > the mid position
882 if (newPosition < d->m_MaximumPosition)
883 this->setPositions(d->m_MinimumPosition, newPosition, d->m_MaximumPosition);
884 else
885 this->setPositions(d->m_MinimumPosition, d->m_MidPosition, newPosition);
886 }
887 else if (this->isMinimumSliderDown())
888 {
889 double newMinPos = qMin(newPosition, d->m_MidPosition);
890 this->setPositions(newMinPos, d->m_MidPosition, d->m_MaximumPosition);
891 }
892 // The upper/right slider is down
893 else if (this->isMaximumSliderDown())
894 {
895 double newMaxPos = qMax(d->m_MidPosition, newPosition);
896 this->setPositions(d->m_MinimumPosition, d->m_MidPosition, newMaxPos);
897 }
898 // The mid slider is down
899 else if (this->isMidSliderDown())
900 {
901 double newMidPos = qMin(qMax(d->m_MinimumPosition, newPosition), d->m_MaximumPosition);
902 this->setPositions(d->m_MinimumPosition, newMidPos, d->m_MaximumPosition);
903 }
904 mouseEvent->accept();
905}
906
907// --------------------------------------------------------------------------
908// Standard Qt UI mouseEvents
909void ctk3Slider::mouseReleaseEvent(QMouseEvent* mouseEvent)
910{
912 this->QSlider::mouseReleaseEvent(mouseEvent);
913
914 setSliderDown(false);
915 d->m_SelectedHandles = ctk3SliderPrivate::NoHandle;
916
917 emit released(d->m_MinimumValue, d->m_MidValue, d->m_MaximumValue);
918 this->update();
919}
920
921// --------------------------------------------------------------------------
923{
924 Q_D(const ctk3Slider);
925 return d->m_SelectedHandles & ctk3SliderPrivate::MinimumHandle;
926}
927
928// --------------------------------------------------------------------------
930{
931 Q_D(const ctk3Slider);
932 return d->m_SelectedHandles & ctk3SliderPrivate::MaximumHandle;
933}
934// --------------------------------------------------------------------------
936{
937 Q_D(const ctk3Slider);
938 return d->m_SelectedHandles & ctk3SliderPrivate::MidHandle;
939}
940
941// --------------------------------------------------------------------------
942void ctk3Slider::initMinimumSliderStyleOption(QStyleOptionSlider* option) const
943{
944 this->initStyleOption(option);
945}
946
947// --------------------------------------------------------------------------
948void ctk3Slider::initMaximumSliderStyleOption(QStyleOptionSlider* option) const
949{
950 this->initStyleOption(option);
951}
952// --------------------------------------------------------------------------
953void ctk3Slider::initMidSliderStyleOption(QStyleOptionSlider* option) const
954{
955 this->initStyleOption(option);
956}
957
958// --------------------------------------------------------------------------
959QString ctk3Slider::handleToolTip()const
960{
961 Q_D(const ctk3Slider);
962 return d->m_HandleToolTip;
963}
964
965// --------------------------------------------------------------------------
966void ctk3Slider::setHandleToolTip(const QString &_toolTip)
967{
969 d->m_HandleToolTip = _toolTip;
970}
971
972// --------------------------------------------------------------------------
973bool ctk3Slider::event(QEvent* _event)
974{
976 switch(_event->type())
977 {
978 case QEvent::ToolTip:
979 {
980 QHelpEvent* helpEvent = static_cast<QHelpEvent*>(_event);
982 // Test the MinimumHandle
983 opt.sliderPosition = d->m_MinimumPosition;
984 opt.sliderValue = d->m_MinimumValue;
985 this->initStyleOption(&opt);
986 QStyle::SubControl hoveredControl =
988 QStyle::CC_Slider, &opt, helpEvent->pos(), this);
989 if (!d->m_HandleToolTip.isEmpty() &&
990 hoveredControl == QStyle::SC_SliderHandle)
991 {
992 QToolTip::showText(helpEvent->globalPos(), d->m_HandleToolTip.arg(this->minimumValue()));
993 _event->accept();
994 return true;
995 }
996 // Test the MaximumHandle
997 opt.sliderPosition = d->m_MaximumPosition;
998 opt.sliderValue = d->m_MaximumValue;
999 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}
A ctk3Slider is a slider that lets you input 2 values instead of one (see QSlider).
Definition ctk3slider.h:31
void maximumPositionChanged(int max)
This signal is emitted when sliderDown is true and the slider moves.
void setValues(int min, int mid, int max)
Utility function that set the minimum value and maximum value at once.
void setMaximumValue(int max)
This property holds the slider's current maximum value.
void minimumPositionChanged(int min)
This signal is emitted when sliderDown is true and the slider moves.
void maximumValueChanged(int max)
This signal is emitted when the slider maximum value has changed, with the new slider value as argume...
void minimumValueChanged(int min)
This signal is emitted when the slider minimum value has changed, with the new slider value as argume...
void positionsChanged(int min, int mid, int max)
Utility signal that is fired when minimum or maximum positions have changed.
bool isMaximumSliderDown() const
Returns true if the maximum value handle is down, false if it is up.
void setPositions(int min, int mid, int max)
Utility function that set the minimum, mid and maximum position at once.
ctk3Slider(Qt::Orientation o, QWidget *par=0)
Constructor, builds a ctk3Slider that ranges from 0 to 100 and has a lower and upper values of 0 and ...
void setMinimumValue(int min)
This property holds the slider's current minimum value.
void setMidValue(int mid)
This property holds the slider's current mid value.
bool isMidSliderDown() const
Returns true if the mid value handle is down, false if it is up.
void midValueChanged(int mid)
This signal is emitted when the slider mid value has changed, with the new slider value as argument.
void midPositionChanged(int max)
This signal is emitted when sliderDown is true and the slider moves.
void valuesChanged(int min, int mid, int max)
Utility signal that is fired when minimum, mid or maximum values have changed.
bool isMinimumSliderDown() const
Returns true if the minimum value handle is down, false if it is up.
bool isSliderDown() const const
bool hasTracking() const const
void triggerAction(SliderAction action)
QColor darker(int factor) const const
QColor lighter(int factor) const const
void accept()
void ignore()
Type type() const const
void setColorAt(qreal position, const QColor &color)
const QPoint & globalPos() const const
const QPoint & pos() const const
QPoint pos() const const
void drawRect(const QRect &rectangle)
void setBrush(Qt::BrushStyle style)
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setPen(Qt::PenStyle style)
int x() const const
int y() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
int bottom() const const
QPoint center() const const
bool contains(const QPoint &point, bool proper) const const
int height() const const
QRect intersected(const QRect &rectangle) const const
int left() const const
int right() const const
int top() const const
QRect united(const QRect &rectangle) const const
int width() const const
int x() const const
int y() const const
Qt::MouseButton button() const const
Qt::MouseButtons buttons() const const
virtual bool event(QEvent *event) override
virtual void initStyleOption(QStyleOptionSlider *option) const const
virtual void mouseReleaseEvent(QMouseEvent *ev) override
PM_MaximumDragDistance
virtual SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, const QPoint &position, const QWidget *widget) const const=0
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
int sliderPositionFromValue(int min, int max, int logicalValue, int span, bool upsideDown)
int sliderValueFromPosition(int min, int max, int position, int span, bool upsideDown)
virtual QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const const=0
void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex &option)
Horizontal
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
QStyle * style() const const
void update()
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 13 2024 11:47:10 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.