QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsrangeslider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrangeslider.cpp
3 ---------------------
4 begin : November 2020
5 copyright : (C) 2020 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsrangeslider.h"
17#include <QPainter>
18#include <QMouseEvent>
19
21 : QgsRangeSlider( Qt::Horizontal, parent )
22{
23}
24
25QgsRangeSlider::QgsRangeSlider( Qt::Orientation orientation, QWidget *parent )
26 : QWidget( parent )
27{
28 mStyleOption.minimum = 0;
29 mStyleOption.maximum = 100;
30 mStyleOption.orientation = orientation;
31
32 setFocusPolicy( Qt::FocusPolicy( style()->styleHint( QStyle::SH_Button_FocusPolicy ) ) );
33 QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::Slider );
34 if ( mStyleOption.orientation == Qt::Vertical )
35 sp.transpose();
36 setSizePolicy( sp );
37 setAttribute( Qt::WA_WState_OwnSizePolicy, false );
38
39 setAttribute( Qt::WA_Hover );
40 setMouseTracking( true );
41}
42
44{
45 return mStyleOption.maximum;
46}
47
48void QgsRangeSlider::setMaximum( int maximum )
49{
50 if ( mStyleOption.maximum == maximum )
51 return;
52
53 mStyleOption.maximum = maximum;
54 mStyleOption.minimum = std::min( maximum, mStyleOption.minimum );
55 emit rangeLimitsChanged( mStyleOption.minimum, mStyleOption.maximum );
56
57 if ( mUpperValue > maximum || mLowerValue > maximum )
58 {
59 mUpperValue = std::min( mUpperValue, maximum );
60 mLowerValue = std::min( mLowerValue, maximum );
61 emit rangeChanged( mLowerValue, mUpperValue );
62 }
63
64 update();
65}
66
68{
69 return mStyleOption.minimum;
70}
71
72void QgsRangeSlider::setMinimum( int minimum )
73{
74 if ( mStyleOption.minimum == minimum )
75 return;
76 mStyleOption.minimum = minimum;
77 mStyleOption.maximum = std::max( minimum, mStyleOption.maximum );
78 emit rangeLimitsChanged( mStyleOption.minimum, mStyleOption.maximum );
79
80 if ( mUpperValue < minimum || mLowerValue < minimum )
81 {
82 mUpperValue = std::max( mUpperValue, minimum );
83 mLowerValue = std::max( mLowerValue, minimum );
84 emit rangeChanged( mLowerValue, mUpperValue );
85 }
86
87 update();
88}
89
90void QgsRangeSlider::setRangeLimits( int minimum, int maximum )
91{
92 if ( maximum < minimum )
93 std::swap( minimum, maximum );
94
95 if ( mStyleOption.minimum == minimum && mStyleOption.maximum == maximum )
96 return;
97
98 mStyleOption.minimum = minimum;
99 mStyleOption.maximum = maximum;
100 emit rangeLimitsChanged( mStyleOption.minimum, mStyleOption.maximum );
101
102 if ( mUpperValue < minimum || mLowerValue < minimum || mUpperValue > maximum || mLowerValue > maximum )
103 {
104 mUpperValue = std::min( maximum, std::max( mUpperValue, minimum ) );
105 mLowerValue = std::min( maximum, std::max( mLowerValue, minimum ) );
106 emit rangeChanged( mLowerValue, mUpperValue );
107 }
108
109 update();
110}
111
113{
114 return mLowerValue;
115}
116
117void QgsRangeSlider::setLowerValue( int lowerValue )
118{
119 if ( lowerValue == mLowerValue )
120 return;
121
122 mLowerValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, lowerValue ) );
123 mUpperValue = std::max( mLowerValue, mUpperValue );
124 emit rangeChanged( mLowerValue, mUpperValue );
125 update();
126}
127
129{
130 return mUpperValue;
131}
132
133
134void QgsRangeSlider::setUpperValue( int upperValue )
135{
136 if ( upperValue == mUpperValue )
137 return;
138
139 mUpperValue = std::max( mStyleOption.minimum, std::min( mStyleOption.maximum, upperValue ) );
140 mLowerValue = std::min( mLowerValue, mUpperValue );
141 emit rangeChanged( mLowerValue, mUpperValue );
142 update();
143}
144
145void QgsRangeSlider::setRange( int lower, int upper )
146{
147 if ( lower == mLowerValue && upper == mUpperValue )
148 return;
149
150 if ( upper < lower )
151 std::swap( lower, upper );
152
153 mLowerValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, lower ) );
154 mUpperValue = std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, upper ) );
155 emit rangeChanged( mLowerValue, mUpperValue );
156 update();
157}
158
159bool QgsRangeSlider::event( QEvent *event )
160{
161 switch ( event->type() )
162 {
163 case QEvent::HoverEnter:
164 case QEvent::HoverLeave:
165 case QEvent::HoverMove:
166 if ( const QHoverEvent *he = static_cast<const QHoverEvent *>( event ) )
167 updateHoverControl( he->pos() );
168 break;
169 default:
170 break;
171 }
172 return QWidget::event( event );
173}
174
175int QgsRangeSlider::pick( const QPoint &pt ) const
176{
177 return mStyleOption.orientation == Qt::Horizontal ? pt.x() : pt.y();
178}
179
180int QgsRangeSlider::pixelPosToRangeValue( int pos ) const
181{
182 const QRect gr = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderGroove, this );
183 const QRect sr = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
184 int sliderMin, sliderMax, sliderLength;
185 if ( mStyleOption.orientation == Qt::Horizontal )
186 {
187 sliderLength = sr.width();
188 sliderMin = gr.x();
189 sliderMax = gr.right() - sliderLength + 1;
190 }
191 else
192 {
193 sliderLength = sr.height();
194 sliderMin = gr.y();
195 sliderMax = gr.bottom() - sliderLength + 1;
196 }
197
198 int value = QStyle::sliderValueFromPosition( mStyleOption.minimum, mStyleOption.maximum, pos - sliderMin,
199 sliderMax - sliderMin );
200 if ( mFlipped )
201 value = mStyleOption.maximum + mStyleOption.minimum - value;
202 return value;
203}
204
205bool QgsRangeSlider::updateHoverControl( const QPoint &pos )
206{
207 const QRect lastHoverRect = mHoverRect;
208 const bool doesHover = testAttribute( Qt::WA_Hover );
209 if ( doesHover && newHoverControl( pos ) )
210 {
211 update( lastHoverRect );
212 update( mHoverRect );
213 return true;
214 }
215 return !doesHover;
216}
217
218bool QgsRangeSlider::newHoverControl( const QPoint &pos )
219{
220 const Control lastHoverControl = mHoverControl;
221 const QStyle::SubControl lastHoverSubControl = mHoverSubControl;
222
223 mStyleOption.subControls = QStyle::SC_All;
224
225 mStyleOption.sliderPosition = unFlippedSliderPosition( mLowerValue );
226 const QRect lowerHandleRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
227 mStyleOption.sliderPosition = unFlippedSliderPosition( mUpperValue );
228 const QRect upperHandleRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
229
230 const QRect grooveRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderGroove, this );
231 const QRect tickmarksRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderTickmarks, this );
232 if ( lowerHandleRect.contains( pos ) )
233 {
234 mHoverRect = lowerHandleRect;
235 mHoverControl = Lower;
236 mHoverSubControl = QStyle::SC_SliderHandle;
237 setCursor( Qt::OpenHandCursor );
238 }
239 else if ( upperHandleRect.contains( pos ) )
240 {
241 mHoverRect = upperHandleRect;
242 mHoverControl = Upper;
243 mHoverSubControl = QStyle::SC_SliderHandle;
244 setCursor( Qt::OpenHandCursor );
245 }
246 else if ( grooveRect.contains( pos ) )
247 {
248 mHoverRect = grooveRect;
249 mHoverControl = None;
250 mHoverSubControl = QStyle::SC_SliderGroove;
251
252 if ( selectedRangeRect().contains( pos ) )
253 setCursor( Qt::OpenHandCursor );
254 else
255 unsetCursor();
256 }
257 else if ( tickmarksRect.contains( pos ) )
258 {
259 mHoverRect = tickmarksRect;
260 mHoverControl = None;
261 mHoverSubControl = QStyle::SC_SliderTickmarks;
262 unsetCursor();
263 }
264 else
265 {
266 mHoverRect = QRect();
267 mHoverControl = None;
268 mHoverSubControl = QStyle::SC_None;
269 unsetCursor();
270 }
271 return mHoverSubControl != lastHoverSubControl || mHoverControl != lastHoverControl;
272}
273
274QRect QgsRangeSlider::selectedRangeRect()
275{
276 QRect selectionRect;
277
278 mStyleOption.activeSubControls = mHoverControl == Lower || mActiveControl == Lower ? QStyle::SC_SliderHandle : QStyle::SC_None;
279 mStyleOption.sliderPosition = unFlippedSliderPosition( mLowerValue );
280 const QRect lowerHandleRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, nullptr );
281
282 mStyleOption.activeSubControls = mHoverControl == Upper || mActiveControl == Lower ? QStyle::SC_SliderHandle : QStyle::SC_None;
283 mStyleOption.sliderPosition = unFlippedSliderPosition( mUpperValue );
284 const QRect upperHandleRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, nullptr );
285
286 const QRect grooveRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderGroove, nullptr );
287
288 switch ( mStyleOption.orientation )
289 {
290 case Qt::Horizontal:
291 selectionRect = mFlipped ? QRect( upperHandleRect.right(),
292 grooveRect.y(),
293 lowerHandleRect.left() - upperHandleRect.right(),
294 grooveRect.height()
295 )
296 : QRect( lowerHandleRect.right(),
297 grooveRect.y(),
298 upperHandleRect.left() - lowerHandleRect.right(),
299 grooveRect.height()
300 );
301 break;
302
303 case Qt::Vertical:
304 selectionRect = mFlipped ? QRect( grooveRect.x(),
305 lowerHandleRect.top(),
306 grooveRect.width(),
307 upperHandleRect.bottom() - lowerHandleRect.top()
308 )
309 : QRect( grooveRect.x(),
310 upperHandleRect.top(),
311 grooveRect.width(),
312 lowerHandleRect.bottom() - upperHandleRect.top()
313 );
314 break;
315 }
316
317 return selectionRect.adjusted( -1, 1, 1, -1 );
318}
319
320void QgsRangeSlider::applyStep( int step )
321{
322 switch ( mFocusControl )
323 {
324 case Lower:
325 {
326 const int newLowerValue = std::min( mUpperValue, std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, mLowerValue + step ) ) );
327 if ( newLowerValue != mLowerValue )
328 {
329 mLowerValue = newLowerValue;
330 emit rangeChanged( mLowerValue, mUpperValue );
331 update();
332 }
333 break;
334 }
335
336 case Upper:
337 {
338 const int newUpperValue = std::max( mLowerValue, std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, mUpperValue + step ) ) );
339 if ( newUpperValue != mUpperValue )
340 {
341 mUpperValue = newUpperValue;
342 emit rangeChanged( mLowerValue, mUpperValue );
343 update();
344 }
345 break;
346 }
347
348 case Range:
349 {
350 if ( step < 0 )
351 {
352 const int previousWidth = mUpperValue - mLowerValue;
353 const int newLowerValue = std::min( mUpperValue, std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, mLowerValue + step ) ) );
354 if ( newLowerValue != mLowerValue )
355 {
356 mLowerValue = newLowerValue;
357 mUpperValue = std::min( mStyleOption.maximum, mLowerValue + previousWidth );
358 emit rangeChanged( mLowerValue, mUpperValue );
359 update();
360 }
361 }
362 else
363 {
364 const int previousWidth = mUpperValue - mLowerValue;
365 const int newUpperValue = std::max( mLowerValue, std::min( mStyleOption.maximum, std::max( mStyleOption.minimum, mUpperValue + step ) ) );
366 if ( newUpperValue != mUpperValue )
367 {
368 mUpperValue = newUpperValue;
369 mLowerValue = std::max( mStyleOption.minimum, mUpperValue - previousWidth );
370 emit rangeChanged( mLowerValue, mUpperValue );
371 update();
372 }
373 }
374 break;
375 }
376
377 case None:
378 case Both:
379 break;
380 }
381}
382
383int QgsRangeSlider::unFlippedSliderPosition( int value ) const
384{
385 return mFlipped ? mStyleOption.maximum + mStyleOption.minimum - value : value;
386}
387
389{
390 return mPageStep;
391}
392
394{
395 mPageStep = step;
396}
397
399{
400 return mSingleStep;
401}
402
404{
405 mSingleStep = step;
406}
407
408void QgsRangeSlider::setTickPosition( QSlider::TickPosition position )
409{
410 mStyleOption.tickPosition = position;
411 update();
412}
413
414QSlider::TickPosition QgsRangeSlider::tickPosition() const
415{
416 return mStyleOption.tickPosition;
417}
418
420{
421 mStyleOption.tickInterval = interval;
422 update();
423}
424
426{
427 return mStyleOption.tickInterval;
428}
429
430void QgsRangeSlider::setOrientation( Qt::Orientation orientation )
431{
432 mStyleOption.orientation = orientation;
433 if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
434 {
435 setSizePolicy( sizePolicy().transposed() );
436 setAttribute( Qt::WA_WState_OwnSizePolicy, false );
437 }
438 update();
439 updateGeometry();
440}
441
442Qt::Orientation QgsRangeSlider::orientation() const
443{
444 return mStyleOption.orientation;
445}
446
448{
449 return mFlipped;
450}
451
453{
454 mFlipped = flipped;
455 update();
456}
457
458void QgsRangeSlider::paintEvent( QPaintEvent * )
459{
460 QPainter painter( this );
461
462 mStyleOption.initFrom( this );
463 mStyleOption.rect = rect();
464 mStyleOption.sliderPosition = mStyleOption.minimum;
465 mStyleOption.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderTickmarks;
466
467 mStyleOption.activeSubControls = mHoverSubControl;
468 // draw groove
469 style()->drawComplexControl( QStyle::CC_Slider, &mStyleOption, &painter );
470
471 QColor color = palette().color( QPalette::Highlight );
472 color.setAlpha( 160 );
473 painter.setBrush( QBrush( color ) );
474 painter.setPen( Qt::NoPen );
475 painter.drawRect( selectedRangeRect() );
476
477 // draw first handle
478 mStyleOption.subControls = QStyle::SC_SliderHandle;
479 mStyleOption.activeSubControls = mHoverControl == Lower || mActiveControl == Lower ? QStyle::SC_SliderHandle : QStyle::SC_None;
480 mStyleOption.sliderPosition = unFlippedSliderPosition( mLowerValue );
481 if ( mActiveControl == Lower )
482 mStyleOption.state |= QStyle::State_Sunken;
483 else
484 mStyleOption.state &= ~QStyle::State_Sunken;
485 style()->drawComplexControl( QStyle::CC_Slider, &mStyleOption, &painter );
486
487 // draw second handle
488 mStyleOption.activeSubControls = mHoverControl == Upper || mActiveControl == Lower ? QStyle::SC_SliderHandle : QStyle::SC_None;
489 mStyleOption.sliderPosition = unFlippedSliderPosition( mUpperValue );
490 if ( mActiveControl == Upper )
491 mStyleOption.state |= QStyle::State_Sunken;
492 else
493 mStyleOption.state &= ~QStyle::State_Sunken;
494 style()->drawComplexControl( QStyle::CC_Slider, &mStyleOption, &painter );
495
496 if ( hasFocus() && mFocusControl != None )
497 {
498 //draw focus rect
499 QStyleOptionFocusRect option;
500 option.initFrom( this );
501 option.state = QStyle::State_KeyboardFocusChange;
502 if ( mFocusControl == Lower )
503 {
504 mStyleOption.sliderPosition = unFlippedSliderPosition( mLowerValue );
505 option.rect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
506 }
507 else if ( mFocusControl == Upper )
508 {
509 mStyleOption.sliderPosition = unFlippedSliderPosition( mUpperValue );
510 option.rect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
511 }
512 else if ( mFocusControl == Range )
513 {
514 option.rect = selectedRangeRect();
515 if ( mStyleOption.orientation == Qt::Horizontal )
516 option.rect = option.rect.adjusted( 0, -1, 0, 1 );
517 else
518 option.rect = option.rect.adjusted( -1, 0, 1, 0 );
519 }
520 style()->drawPrimitive( QStyle::PE_FrameFocusRect, &option, &painter );
521 }
522}
523
524void QgsRangeSlider::mousePressEvent( QMouseEvent *event )
525{
526 if ( mStyleOption.maximum == mStyleOption.minimum || ( event->buttons() ^ event->button() ) )
527 {
528 event->ignore();
529 return;
530 }
531
532 event->accept();
533
534 mStyleOption.sliderPosition = unFlippedSliderPosition( mLowerValue );
535 const bool overLowerControl = style()->hitTestComplexControl( QStyle::CC_Slider, &mStyleOption, event->pos(), this ) == QStyle::SC_SliderHandle;
536 const QRect lowerSliderRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
537 mStyleOption.sliderPosition = unFlippedSliderPosition( mUpperValue );
538 const bool overUpperControl = style()->hitTestComplexControl( QStyle::CC_Slider, &mStyleOption, event->pos(), this ) == QStyle::SC_SliderHandle;
539 const QRect upperSliderRect = style()->subControlRect( QStyle::CC_Slider, &mStyleOption, QStyle::SC_SliderHandle, this );
540
541 const bool overSelectedRange = selectedRangeRect().contains( event->pos() );
542
543 mLowerClickOffset = pick( event->pos() - lowerSliderRect.topLeft() );
544 mUpperClickOffset = pick( event->pos() - upperSliderRect.topLeft() );
545
546 mPreDragLowerValue = mLowerValue;
547 mPreDragUpperValue = mUpperValue;
548 mRangeDragOffset = 0;
549
550 if ( ( overLowerControl || overUpperControl ) && event->modifiers() & Qt::ShiftModifier )
551 {
552 mActiveControl = Range; // shift + drag over handle moves the whole range
553 mRangeDragOffset = overUpperControl ? mUpperClickOffset : mLowerClickOffset;
554 mFocusControl = overUpperControl ? Upper : Lower;
555 }
556 else if ( overLowerControl && overUpperControl )
557 mActiveControl = Both;
558 else if ( overLowerControl )
559 {
560 mActiveControl = Lower;
561 mFocusControl = Lower;
562 }
563 else if ( overUpperControl )
564 {
565 mActiveControl = Upper;
566 mFocusControl = Upper;
567 }
568 else if ( overSelectedRange )
569 {
570 mActiveControl = Range;
571 mFocusControl = Range;
572 }
573 else
574 mActiveControl = None;
575
576 if ( mActiveControl != None )
577 {
578 mStartDragPos = pixelPosToRangeValue( pick( event->pos() ) - mRangeDragOffset );
579 }
580}
581
582void QgsRangeSlider::mouseMoveEvent( QMouseEvent *event )
583{
584 if ( mActiveControl == None )
585 {
586 event->ignore();
587 return;
588 }
589
590 event->accept();
591
592 int newPosition = pixelPosToRangeValue( pick( event->pos() ) );
593
594 bool changed = false;
595 Control destControl = mActiveControl;
596 if ( destControl == Both )
597 {
598 // if click was over both handles, then the direction of the drag changes which control is affected
599 if ( newPosition < mStartDragPos )
600 {
601 destControl = Lower;
602 mFocusControl = Lower;
603 if ( mUpperValue != mPreDragUpperValue )
604 {
605 changed = true;
606 mUpperValue = mPreDragUpperValue;
607 }
608 }
609 else if ( newPosition > mStartDragPos )
610 {
611 destControl = Upper;
612 mFocusControl = Upper;
613 if ( mLowerValue != mPreDragLowerValue )
614 {
615 changed = true;
616 mLowerValue = mPreDragLowerValue;
617 }
618 }
619 else
620 {
621 destControl = None;
622 if ( mUpperValue != mPreDragUpperValue )
623 {
624 changed = true;
625 mUpperValue = mPreDragUpperValue;
626 }
627 if ( mLowerValue != mPreDragLowerValue )
628 {
629 changed = true;
630 mLowerValue = mPreDragLowerValue;
631 }
632 }
633 }
634
635 switch ( destControl )
636 {
637 case None:
638 case Both:
639 break;
640
641 case Lower:
642 {
643 // adjust value to account for lower handle click offset
644 newPosition = std::min( mUpperValue, pixelPosToRangeValue( pick( event->pos() ) - mLowerClickOffset ) );
645 if ( mLowerValue != newPosition )
646 {
647 mLowerValue = newPosition;
648 changed = true;
649 }
650 break;
651 }
652
653 case Upper:
654 {
655 // adjust value to account for upper handle click offset
656 newPosition = std::max( mLowerValue, pixelPosToRangeValue( pick( event->pos() ) - mUpperClickOffset ) );
657 if ( mUpperValue != newPosition )
658 {
659 mUpperValue = newPosition;
660 changed = true;
661 }
662 break;
663 }
664
665 case Range:
666 {
667 newPosition = pixelPosToRangeValue( pick( event->pos() ) - mRangeDragOffset ) ;
668 int delta = newPosition - mStartDragPos;
669
670 if ( delta > 0 )
671 {
672 // move range up
673 const int maxDelta = mStyleOption.maximum - mPreDragUpperValue;
674 delta = std::min( maxDelta, delta );
675 mLowerValue = mPreDragLowerValue + delta;
676 mUpperValue = mPreDragUpperValue + delta;
677 changed = true;
678 }
679 else if ( delta < 0 )
680 {
681 // move range down
682 delta = -delta;
683 const int maxDelta = mPreDragLowerValue - mStyleOption.minimum ;
684 delta = std::min( maxDelta, delta );
685 mLowerValue = mPreDragLowerValue - delta;
686 mUpperValue = mPreDragUpperValue - delta;
687 changed = true;
688 }
689
690 break;
691 }
692 }
693
694 if ( changed )
695 {
696 update();
697 emit rangeChanged( mLowerValue, mUpperValue );
698 }
699}
700
701void QgsRangeSlider::mouseReleaseEvent( QMouseEvent *event )
702{
703 if ( mActiveControl == None || event->buttons() )
704 {
705 event->ignore();
706 return;
707 }
708
709 event->accept();
710 mActiveControl = None;
711 update();
712}
713
714void QgsRangeSlider::keyPressEvent( QKeyEvent *event )
715{
716 Control destControl = mFocusControl;
717 if ( ( destControl == Lower || destControl == Upper ) && mLowerValue == mUpperValue )
718 destControl = Both; //ambiguous destination, because both sliders are on top of each other
719
720 switch ( event->key() )
721 {
722 case Qt::Key_Left:
723 {
724 switch ( mStyleOption.orientation )
725 {
726 case Qt::Horizontal:
727 if ( destControl == Both )
728 mFocusControl = mFlipped ? Upper : Lower;
729
730 applyStep( mFlipped ? mSingleStep : -mSingleStep );
731 break;
732
733 case Qt::Vertical:
734 if ( mFlipped )
735 {
736 switch ( mFocusControl )
737 {
738 case Lower:
739 mFocusControl = Range;
740 break;
741 case Range:
742 mFocusControl = Upper;
743 break;
744 case Upper:
745 case None:
746 case Both:
747 mFocusControl = Lower;
748 break;
749 }
750 }
751 else
752 {
753 switch ( mFocusControl )
754 {
755 case Lower:
756 case None:
757 case Both:
758 mFocusControl = Upper;
759 break;
760 case Range:
761 mFocusControl = Lower;
762 break;
763 case Upper:
764 mFocusControl = Range;
765 break;
766 }
767 }
768 update();
769 break;
770 }
771 break;
772 }
773
774 case Qt::Key_Right:
775 {
776 switch ( mStyleOption.orientation )
777 {
778 case Qt::Horizontal:
779 if ( destControl == Both )
780 mFocusControl = mFlipped ? Lower : Upper;
781 applyStep( mFlipped ? -mSingleStep : mSingleStep );
782 break;
783
784 case Qt::Vertical:
785 if ( mFlipped )
786 {
787 switch ( mFocusControl )
788 {
789 case Lower:
790 case None:
791 case Both:
792 mFocusControl = Upper;
793 break;
794 case Range:
795 mFocusControl = Lower;
796 break;
797 case Upper:
798 mFocusControl = Range;
799 break;
800 }
801 }
802 else
803 {
804 switch ( mFocusControl )
805 {
806 case Lower:
807 mFocusControl = Range;
808 break;
809 case Range:
810 mFocusControl = Upper;
811 break;
812 case Upper:
813 case None:
814 case Both:
815 mFocusControl = Lower;
816 break;
817 }
818 }
819 update();
820 break;
821 }
822 break;
823 }
824
825 case Qt::Key_Up:
826 {
827 switch ( mStyleOption.orientation )
828 {
829 case Qt::Horizontal:
830 if ( mFlipped )
831 {
832 switch ( mFocusControl )
833 {
834 case Lower:
835 mFocusControl = Range;
836 break;
837 case Range:
838 mFocusControl = Upper;
839 break;
840 case Upper:
841 case None:
842 case Both:
843 mFocusControl = Lower;
844 break;
845 }
846 }
847 else
848 {
849 switch ( mFocusControl )
850 {
851 case Lower:
852 mFocusControl = Upper;
853 break;
854 case Range:
855 case None:
856 case Both:
857 mFocusControl = Lower;
858 break;
859 case Upper:
860 mFocusControl = Range;
861 break;
862 }
863 }
864 update();
865 break;
866
867 case Qt::Vertical:
868 if ( destControl == Both )
869 mFocusControl = mFlipped ? Upper : Lower;
870
871 applyStep( mFlipped ? mSingleStep : -mSingleStep );
872 break;
873 }
874 break;
875 }
876
877 case Qt::Key_Down:
878 {
879 switch ( mStyleOption.orientation )
880 {
881 case Qt::Horizontal:
882 if ( mFlipped )
883 {
884 switch ( mFocusControl )
885 {
886 case Lower:
887 mFocusControl = Upper;
888 break;
889 case Range:
890 case None:
891 case Both:
892 mFocusControl = Lower;
893 break;
894 case Upper:
895 mFocusControl = Range;
896 break;
897 }
898 }
899 else
900 {
901 switch ( mFocusControl )
902 {
903 case Lower:
904 mFocusControl = Range;
905 break;
906 case Range:
907 mFocusControl = Upper;
908 break;
909 case Upper:
910 case None:
911 case Both:
912 mFocusControl = Lower;
913 break;
914 }
915 }
916 update();
917 break;
918
919 case Qt::Vertical:
920 if ( destControl == Both )
921 mFocusControl = mFlipped ? Lower : Upper;
922
923 applyStep( mFlipped ? -mSingleStep : mSingleStep );
924 break;
925 }
926 break;
927 }
928
929 case Qt::Key_PageUp:
930 {
931 switch ( mStyleOption.orientation )
932 {
933 case Qt::Horizontal:
934 if ( destControl == Both )
935 mFocusControl = mFlipped ? Lower : Upper;
936
937 applyStep( mFlipped ? -mPageStep : mPageStep );
938 break;
939
940 case Qt::Vertical:
941 if ( destControl == Both )
942 mFocusControl = mFlipped ? Upper : Lower;
943
944 applyStep( mFlipped ? mPageStep : -mPageStep );
945 break;
946 }
947 break;
948 }
949
950 case Qt::Key_PageDown:
951 {
952 switch ( mStyleOption.orientation )
953 {
954 case Qt::Horizontal:
955 if ( destControl == Both )
956 mFocusControl = mFlipped ? Upper : Lower;
957
958 applyStep( mFlipped ? mPageStep : -mPageStep );
959 break;
960
961 case Qt::Vertical:
962 if ( destControl == Both )
963 mFocusControl = mFlipped ? Lower : Upper;
964
965 applyStep( mFlipped ? -mPageStep : mPageStep );
966 break;
967 }
968 break;
969 }
970
971 case Qt::Key_Home:
972 switch ( destControl )
973 {
974 case Lower:
975 applyStep( mFlipped ? mUpperValue - mLowerValue : mStyleOption.minimum - mLowerValue );
976 break;
977
978 case Upper:
979 applyStep( mFlipped ? mStyleOption.maximum - mUpperValue : mLowerValue - mUpperValue );
980 break;
981
982 case Range:
983 applyStep( mFlipped ? mStyleOption.maximum - mUpperValue : mStyleOption.minimum - mLowerValue );
984 break;
985
986 case Both:
987 if ( destControl == Both )
988 mFocusControl = mFlipped ? Upper : Lower;
989
990 applyStep( mFlipped ? mStyleOption.maximum - mUpperValue : mStyleOption.minimum - mLowerValue );
991 break;
992
993 case None:
994 break;
995 }
996
997 break;
998
999 case Qt::Key_End:
1000 switch ( destControl )
1001 {
1002 case Lower:
1003 applyStep( mFlipped ? mStyleOption.minimum - mLowerValue : mUpperValue - mLowerValue );
1004 break;
1005
1006 case Upper:
1007 applyStep( mFlipped ? mLowerValue - mUpperValue : mStyleOption.maximum - mUpperValue );
1008 break;
1009
1010 case Range:
1011 applyStep( mFlipped ? mStyleOption.minimum - mLowerValue : mStyleOption.maximum - mUpperValue );
1012 break;
1013
1014 case Both:
1015 if ( destControl == Both )
1016 mFocusControl = mFlipped ? Lower : Upper;
1017
1018 applyStep( mFlipped ? mStyleOption.minimum - mLowerValue : mStyleOption.maximum - mUpperValue );
1019 break;
1020
1021 case None:
1022 break;
1023 }
1024 break;
1025
1026 default:
1027 event->ignore();
1028 break;
1029 }
1030}
1031
1033{
1034 ensurePolished();
1035
1036 // these hardcoded magic values look like a hack, but they are taken straight from the Qt QSlider widget code!
1037 static constexpr int SLIDER_LENGTH = 84;
1038 static constexpr int TICK_SPACE = 5;
1039
1040 int thick = style()->pixelMetric( QStyle::PM_SliderThickness, &mStyleOption, this );
1041 if ( mStyleOption.tickPosition & QSlider::TicksAbove )
1042 thick += TICK_SPACE;
1043 if ( mStyleOption.tickPosition & QSlider::TicksBelow )
1044 thick += TICK_SPACE;
1045 int w = thick, h = SLIDER_LENGTH;
1046 if ( mStyleOption.orientation == Qt::Horizontal )
1047 {
1048 std::swap( w, h );
1049 }
1050 return style()->sizeFromContents( QStyle::CT_Slider, &mStyleOption, QSize( w, h ), this );
1051}
1052
1054{
1055 QSize s = sizeHint();
1056 const int length = style()->pixelMetric( QStyle::PM_SliderLength, &mStyleOption, this );
1057 if ( mStyleOption.orientation == Qt::Horizontal )
1058 s.setWidth( length );
1059 else
1060 s.setHeight( length );
1061 return s;
1062}
1063
1064
A slider control with two interactive endpoints, for interactive selection of a range of values.
void setUpperValue(int value)
Sets the upper value for the range currently selected in the widget.
void setRangeLimits(int minimum, int maximum)
Sets the minimum and maximum range limits for values allowed in the widget.
QgsRangeSlider(QWidget *parent=nullptr)
Constructor for QgsRangeSlider, with the specified parent widget.
void setOrientation(Qt::Orientation orientation)
Sets the orientation of the slider.
void setTickInterval(int interval)
Sets the interval for tick marks shown in the widget.
int upperValue() const
Returns the upper value for the range selected in the widget.
int tickInterval() const
Returns the interval for tick marks shown in the widget.
void paintEvent(QPaintEvent *event) override
void rangeLimitsChanged(int minimum, int maximum)
Emitted when the limits of values allowed in the widget is changed.
QSize sizeHint() const override
void keyPressEvent(QKeyEvent *event) override
void mouseMoveEvent(QMouseEvent *event) override
void rangeChanged(int minimum, int maximum)
Emitted when the range selected in the widget is changed.
int maximum() const
Returns the maximum value allowed by the widget.
void mousePressEvent(QMouseEvent *event) override
bool event(QEvent *event) override
void setSingleStep(int step)
Sets the single step value for the widget.
void setMinimum(int minimum)
Sets the minimum value allowed in the widget.
void setPageStep(int step)
Sets the page step value for the widget.
Qt::Orientation orientation() const
Returns the orientation of the slider.
void setMaximum(int maximum)
Sets the maximum value allowed in the widget.
int pageStep() const
Returns the page step value for the widget.
void setFlippedDirection(bool flipped)
Sets whether the slider has its values flipped.
int minimum() const
Returns the minimum value allowed by the widget.
QSize minimumSizeHint() const override
void setRange(int lower, int upper)
Sets the current range selected in the widget.
void setLowerValue(int value)
Sets the lower value for the range currently selected in the widget.
int lowerValue() const
Returns the lower value for the range selected in the widget.
void setTickPosition(QSlider::TickPosition position)
Sets the position of the tick marks shown in the widget.
void mouseReleaseEvent(QMouseEvent *event) override
bool flippedDirection() const
Returns true if the slider has its values flipped.
QSlider::TickPosition tickPosition() const
Returns the position of the tick marks shown in the widget.
int singleStep() const
Returns the single step value for the widget.