QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
25 QgsRangeSlider::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 
48 void 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 
72 void 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 
90 void 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 
117 void 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 
134 void 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 
145 void 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 
159 bool 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 
175 int QgsRangeSlider::pick( const QPoint &pt ) const
176 {
177  return mStyleOption.orientation == Qt::Horizontal ? pt.x() : pt.y();
178 }
179 
180 int 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 
205 bool 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 
218 bool 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 
274 QRect 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 
320 void 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 
383 int 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 
408 void QgsRangeSlider::setTickPosition( QSlider::TickPosition position )
409 {
410  mStyleOption.tickPosition = position;
411  update();
412 }
413 
414 QSlider::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 
430 void 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 
442 Qt::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 
458 void 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 
524 void 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 
582 void 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 
701 void 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 
714 void 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 
QgsRangeSlider::setMinimum
void setMinimum(int minimum)
Sets the minimum value allowed in the widget.
Definition: qgsrangeslider.cpp:72
QgsRangeSlider::setLowerValue
void setLowerValue(int value)
Sets the lower value for the range currently selected in the widget.
Definition: qgsrangeslider.cpp:117
QgsRangeSlider::rangeLimitsChanged
void rangeLimitsChanged(int minimum, int maximum)
Emitted when the limits of values allowed in the widget is changed.
QgsRangeSlider::upperValue
int upperValue() const
Returns the upper value for the range selected in the widget.
Definition: qgsrangeslider.cpp:128
QgsRangeSlider::lowerValue
int lowerValue() const
Returns the lower value for the range selected in the widget.
Definition: qgsrangeslider.cpp:112
QgsRangeSlider::setUpperValue
void setUpperValue(int value)
Sets the upper value for the range currently selected in the widget.
Definition: qgsrangeslider.cpp:134
QgsRangeSlider::paintEvent
void paintEvent(QPaintEvent *event) override
Definition: qgsrangeslider.cpp:458
QgsRangeSlider::sizeHint
QSize sizeHint() const override
Definition: qgsrangeslider.cpp:1032
QgsRangeSlider::event
bool event(QEvent *event) override
Definition: qgsrangeslider.cpp:159
Direction::None
@ None
QgsRangeSlider::pageStep
int pageStep() const
Returns the page step value for the widget.
Definition: qgsrangeslider.cpp:388
QgsRangeSlider::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *event) override
Definition: qgsrangeslider.cpp:582
MathUtils::lower
int ANALYSIS_EXPORT lower(int n, int i)
Lower function.
Definition: MathUtils.cpp:407
QgsRangeSlider::setOrientation
void setOrientation(Qt::Orientation orientation)
Sets the orientation of the slider.
Definition: qgsrangeslider.cpp:430
QgsRangeSlider::tickPosition
QSlider::TickPosition tickPosition() const
Returns the position of the tick marks shown in the widget.
Definition: qgsrangeslider.cpp:414
QgsRangeSlider::keyPressEvent
void keyPressEvent(QKeyEvent *event) override
Definition: qgsrangeslider.cpp:714
QgsRangeSlider::setTickPosition
void setTickPosition(QSlider::TickPosition position)
Sets the position of the tick marks shown in the widget.
Definition: qgsrangeslider.cpp:408
QgsRangeSlider::flippedDirection
bool flippedDirection() const
Returns true if the slider has its values flipped.
Definition: qgsrangeslider.cpp:447
QgsRangeSlider::setMaximum
void setMaximum(int maximum)
Sets the maximum value allowed in the widget.
Definition: qgsrangeslider.cpp:48
QgsRangeSlider::mouseReleaseEvent
void mouseReleaseEvent(QMouseEvent *event) override
Definition: qgsrangeslider.cpp:701
QgsRangeSlider::setPageStep
void setPageStep(int step)
Sets the page step value for the widget.
Definition: qgsrangeslider.cpp:393
QgsRangeSlider::minimum
int minimum() const
Returns the minimum value allowed by the widget.
Definition: qgsrangeslider.cpp:67
QgsRangeSlider::setFlippedDirection
void setFlippedDirection(bool flipped)
Sets whether the slider has its values flipped.
Definition: qgsrangeslider.cpp:452
QgsRangeSlider::QgsRangeSlider
QgsRangeSlider(QWidget *parent=nullptr)
Constructor for QgsRangeSlider, with the specified parent widget.
Definition: qgsrangeslider.cpp:20
QgsRangeSlider::maximum
int maximum() const
Returns the maximum value allowed by the widget.
Definition: qgsrangeslider.cpp:43
QgsRangeSlider::minimumSizeHint
QSize minimumSizeHint() const override
Definition: qgsrangeslider.cpp:1053
QgsRangeSlider::setSingleStep
void setSingleStep(int step)
Sets the single step value for the widget.
Definition: qgsrangeslider.cpp:403
QgsRangeSlider
A slider control with two interactive endpoints, for interactive selection of a range of values.
Definition: qgsrangeslider.h:31
QgsRangeSlider::tickInterval
int tickInterval() const
Returns the interval for tick marks shown in the widget.
Definition: qgsrangeslider.cpp:425
QgsRangeSlider::orientation
Qt::Orientation orientation() const
Returns the orientation of the slider.
Definition: qgsrangeslider.cpp:442
QgsRangeSlider::mousePressEvent
void mousePressEvent(QMouseEvent *event) override
Definition: qgsrangeslider.cpp:524
qgsrangeslider.h
QgsRangeSlider::rangeChanged
void rangeChanged(int minimum, int maximum)
Emitted when the range selected in the widget is changed.
QgsRangeSlider::setTickInterval
void setTickInterval(int interval)
Sets the interval for tick marks shown in the widget.
Definition: qgsrangeslider.cpp:419
QgsRangeSlider::setRangeLimits
void setRangeLimits(int minimum, int maximum)
Sets the minimum and maximum range limits for values allowed in the widget.
Definition: qgsrangeslider.cpp:90
QgsRangeSlider::singleStep
int singleStep() const
Returns the single step value for the widget.
Definition: qgsrangeslider.cpp:398
QgsRangeSlider::setRange
void setRange(int lower, int upper)
Sets the current range selected in the widget.
Definition: qgsrangeslider.cpp:145