Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_slider.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_slider.h"
11#include "qwt_painter.h"
12#include "qwt_scale_draw.h"
13#include "qwt_scale_map.h"
14#include "qwt_math.h"
15#include "qwt.h"
16
17#include <qevent.h>
18#include <qdrawutil.h>
19#include <qpainter.h>
20#include <qstyle.h>
21#include <qstyleoption.h>
22#include <qmargins.h>
23
24static QSize qwtHandleSize( const QSize& size,
25 Qt::Orientation orientation, bool hasTrough )
26{
27 QSize handleSize = size;
28
29 if ( handleSize.isEmpty() )
30 {
31 const int handleThickness = 16;
32 handleSize.setWidth( 2 * handleThickness );
33 handleSize.setHeight( handleThickness );
34
35 if ( !hasTrough )
36 handleSize.transpose();
37
38 if ( orientation == Qt::Vertical )
39 handleSize.transpose();
40 }
41
42 return handleSize;
43}
44
45static QwtScaleDraw::Alignment qwtScaleDrawAlignment(
46 Qt::Orientation orientation, QwtSlider::ScalePosition scalePos )
47{
49
50 if ( orientation == Qt::Vertical )
51 {
52 // NoScale lays out like Left
53 if ( scalePos == QwtSlider::LeadingScale )
55 else
57 }
58 else
59 {
60 // NoScale lays out like Bottom
61 if ( scalePos == QwtSlider::TrailingScale )
63 else
65 }
66
67 return align;
68}
69
70class QwtSlider::PrivateData
71{
72 public:
73 PrivateData()
74 : repeatTimerId( 0 )
75 , updateInterval( 150 )
76 , stepsIncrement( 0 )
77 , pendingValueChange( false )
78 , borderWidth( 2 )
79 , spacing( 4 )
80 , scalePosition( QwtSlider::TrailingScale )
81 , hasTrough( true )
82 , hasGroove( false )
83 , mouseOffset( 0 )
84 {
85 }
86
87 int repeatTimerId;
88 bool timerTick;
89 int updateInterval;
90 int stepsIncrement;
91 bool pendingValueChange;
92
93 QRect sliderRect;
94
95 QSize handleSize;
96 int borderWidth;
97 int spacing;
98
99 Qt::Orientation orientation;
100 QwtSlider::ScalePosition scalePosition;
101
102 bool hasTrough;
103 bool hasGroove;
104
105 int mouseOffset;
106
107 mutable QSize sizeHintCache;
108};
119QwtSlider::QwtSlider( QWidget* parent )
120 : QwtAbstractSlider( parent )
121{
122 initSlider( Qt::Vertical );
123}
124
136QwtSlider::QwtSlider( Qt::Orientation orientation, QWidget* parent )
137 : QwtAbstractSlider( parent )
138{
139 initSlider( orientation );
140}
141
144{
145 delete m_data;
146}
147
148void QwtSlider::initSlider( Qt::Orientation orientation )
149{
150 if ( orientation == Qt::Vertical )
151 setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
152 else
153 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
154
155 setAttribute( Qt::WA_WState_OwnSizePolicy, false );
156
157 m_data = new QwtSlider::PrivateData;
158
159 m_data->orientation = orientation;
160
162 qwtScaleDrawAlignment( orientation, m_data->scalePosition ) );
163 scaleDraw()->setLength( 100 );
164
165 setScale( 0.0, 100.0 );
166 setValue( 0.0 );
167}
168
175void QwtSlider::setOrientation( Qt::Orientation orientation )
176{
177 if ( orientation == m_data->orientation )
178 return;
179
180 m_data->orientation = orientation;
181
183 qwtScaleDrawAlignment( orientation, m_data->scalePosition ) );
184
185 if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
186 {
187 QSizePolicy sp = sizePolicy();
188 sp.transpose();
189 setSizePolicy( sp );
190
191 setAttribute( Qt::WA_WState_OwnSizePolicy, false );
192 }
193
194 if ( testAttribute( Qt::WA_WState_Polished ) )
195 layoutSlider( true );
196}
197
202Qt::Orientation QwtSlider::orientation() const
203{
204 return m_data->orientation;
205}
206
214{
215 if ( m_data->scalePosition == scalePosition )
216 return;
217
218 m_data->scalePosition = scalePosition;
220 qwtScaleDrawAlignment( m_data->orientation, scalePosition ) );
221
222 if ( testAttribute( Qt::WA_WState_Polished ) )
223 layoutSlider( true );
224}
225
231{
232 return m_data->scalePosition;
233}
234
245{
246 if ( width < 0 )
247 width = 0;
248
249 if ( width != m_data->borderWidth )
250 {
251 m_data->borderWidth = width;
252
253 if ( testAttribute( Qt::WA_WState_Polished ) )
254 layoutSlider( true );
255 }
256}
257
263{
264 return m_data->borderWidth;
265}
266
278void QwtSlider::setSpacing( int spacing )
279{
280 if ( spacing <= 0 )
281 spacing = 0;
282
283 if ( spacing != m_data->spacing )
284 {
285 m_data->spacing = spacing;
286
287 if ( testAttribute( Qt::WA_WState_Polished ) )
288 layoutSlider( true );
289 }
290}
291
297{
298 return m_data->spacing;
299}
300
311void QwtSlider::setHandleSize( const QSize& size )
312{
313 if ( size != m_data->handleSize )
314 {
315 m_data->handleSize = size;
316
317 if ( testAttribute( Qt::WA_WState_Polished ) )
318 layoutSlider( true );
319 }
320}
321
327{
328 return m_data->handleSize;
329}
330
345{
346 const QwtScaleDraw* previousScaleDraw = this->scaleDraw();
347 if ( scaleDraw == NULL || scaleDraw == previousScaleDraw )
348 return;
349
350 if ( previousScaleDraw )
351 scaleDraw->setAlignment( previousScaleDraw->alignment() );
352
354
355 if ( testAttribute( Qt::WA_WState_Polished ) )
356 layoutSlider( true );
357}
358
364{
365 return static_cast< const QwtScaleDraw* >( abstractScaleDraw() );
366}
367
373{
374 return static_cast< QwtScaleDraw* >( abstractScaleDraw() );
375}
376
379{
381
382 if ( testAttribute( Qt::WA_WState_Polished ) )
383 layoutSlider( true );
384}
385
396{
397 m_data->updateInterval = qMax( interval, 50 );
398}
399
405{
406 return m_data->updateInterval;
407}
408
416 QPainter* painter, const QRect& sliderRect ) const
417{
418 QRect innerRect( sliderRect );
419
420 if ( m_data->hasTrough )
421 {
422 const int bw = m_data->borderWidth;
423 innerRect = sliderRect.adjusted( bw, bw, -bw, -bw );
424
425 painter->fillRect( innerRect, palette().brush( QPalette::Mid ) );
426 qDrawShadePanel( painter, sliderRect, palette(), true, bw, NULL );
427 }
428
429 if ( m_data->hasGroove )
430 {
431 const QSize handleSize = qwtHandleSize( m_data->handleSize,
432 m_data->orientation, m_data->hasTrough );
433
434 const int slotExtent = 4;
435 const int slotMargin = 4;
436
437 QRect slotRect;
438 if ( orientation() == Qt::Horizontal )
439 {
440 int slotOffset = qMax( 1, handleSize.width() / 2 - slotMargin );
441 int slotHeight = slotExtent + ( innerRect.height() % 2 );
442
443 slotRect.setWidth( innerRect.width() - 2 * slotOffset );
444 slotRect.setHeight( slotHeight );
445 }
446 else
447 {
448 int slotOffset = qMax( 1, handleSize.height() / 2 - slotMargin );
449 int slotWidth = slotExtent + ( innerRect.width() % 2 );
450
451 slotRect.setWidth( slotWidth );
452 slotRect.setHeight( innerRect.height() - 2 * slotOffset );
453
454 }
455
456 slotRect.moveCenter( innerRect.center() );
457
458 QBrush brush = palette().brush( QPalette::Dark );
459 qDrawShadePanel( painter, slotRect, palette(), true, 1, &brush );
460 }
461
462 if ( isValid() )
463 drawHandle( painter, handleRect(), transform( value() ) );
464}
465
473void QwtSlider::drawHandle( QPainter* painter,
474 const QRect& handleRect, int pos ) const
475{
476 const int bw = m_data->borderWidth;
477
478 qDrawShadePanel( painter,
479 handleRect, palette(), false, bw,
480 &palette().brush( QPalette::Button ) );
481
482 pos++; // shade line points one pixel below
483 if ( orientation() == Qt::Horizontal )
484 {
485 qDrawShadeLine( painter, pos, handleRect.top() + bw,
486 pos, handleRect.bottom() - bw, palette(), true, 1 );
487 }
488 else // Vertical
489 {
490 qDrawShadeLine( painter, handleRect.left() + bw, pos,
491 handleRect.right() - bw, pos, palette(), true, 1 );
492 }
493}
494
503bool QwtSlider::isScrollPosition( const QPoint& pos ) const
504{
505 if ( handleRect().contains( pos ) )
506 {
507 const double v = ( orientation() == Qt::Horizontal )
508 ? pos.x() : pos.y();
509
510 m_data->mouseOffset = v - transform( value() );
511 return true;
512 }
513
514 return false;
515}
516
526double QwtSlider::scrolledTo( const QPoint& pos ) const
527{
528 int p = ( orientation() == Qt::Horizontal )
529 ? pos.x() : pos.y();
530
531 p -= m_data->mouseOffset;
532
533 int min = transform( lowerBound() );
534 int max = transform( upperBound() );
535 if ( min > max )
536 qSwap( min, max );
537
538 p = qBound( min, p, max );
539
540 return scaleMap().invTransform( p );
541}
542
547void QwtSlider::mousePressEvent( QMouseEvent* event )
548{
549 if ( isReadOnly() )
550 {
551 event->ignore();
552 return;
553 }
554
555 const QPoint pos = event->pos();
556
557 if ( isValid() && m_data->sliderRect.contains( pos ) )
558 {
559 if ( !handleRect().contains( pos ) )
560 {
561 const int markerPos = transform( value() );
562
563 m_data->stepsIncrement = pageSteps();
564
565 if ( m_data->orientation == Qt::Horizontal )
566 {
567 if ( pos.x() < markerPos )
568 m_data->stepsIncrement = -m_data->stepsIncrement;
569 }
570 else
571 {
572 if ( pos.y() < markerPos )
573 m_data->stepsIncrement = -m_data->stepsIncrement;
574 }
575
576 if ( isInverted() )
577 m_data->stepsIncrement = -m_data->stepsIncrement;
578
579 const double v = value();
580 incrementValue( m_data->stepsIncrement );
581
582 if ( v != value() )
583 {
584 if ( isTracking() )
585 Q_EMIT valueChanged( value() );
586 else
587 m_data->pendingValueChange = true;
588
589 Q_EMIT sliderMoved( value() );
590 }
591
592 m_data->timerTick = false;
593 m_data->repeatTimerId = startTimer( qMax( 250, 2 * updateInterval() ) );
594
595 return;
596 }
597 }
598
600}
601
606void QwtSlider::mouseReleaseEvent( QMouseEvent* event )
607{
608 if ( m_data->repeatTimerId > 0 )
609 {
610 killTimer( m_data->repeatTimerId );
611 m_data->repeatTimerId = 0;
612 m_data->timerTick = false;
613 m_data->stepsIncrement = 0;
614 }
615
616 if ( m_data->pendingValueChange )
617 {
618 m_data->pendingValueChange = false;
619 Q_EMIT valueChanged( value() );
620 }
621
623}
624
633void QwtSlider::timerEvent( QTimerEvent* event )
634{
635 if ( event->timerId() != m_data->repeatTimerId )
636 {
637 QwtAbstractSlider::timerEvent( event );
638 return;
639 }
640
641 if ( !isValid() )
642 {
643 killTimer( m_data->repeatTimerId );
644 m_data->repeatTimerId = 0;
645 return;
646 }
647
648 const double v = value();
649 incrementValue( m_data->stepsIncrement );
650
651 if ( v != value() )
652 {
653 if ( isTracking() )
654 Q_EMIT valueChanged( value() );
655 else
656 m_data->pendingValueChange = true;
657
658 Q_EMIT sliderMoved( value() );
659 }
660
661 if ( !m_data->timerTick )
662 {
663 // restart the timer with a shorter interval
664 killTimer( m_data->repeatTimerId );
665 m_data->repeatTimerId = startTimer( updateInterval() );
666
667 m_data->timerTick = true;
668 }
669}
670
675void QwtSlider::paintEvent( QPaintEvent* event )
676{
677 QPainter painter( this );
678 painter.setClipRegion( event->region() );
679
680 QStyleOption opt;
681 opt.initFrom(this);
682 style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
683
684 if ( m_data->scalePosition != QwtSlider::NoScale )
685 {
686 if ( !m_data->sliderRect.contains( event->rect() ) )
687 scaleDraw()->draw( &painter, palette() );
688 }
689
690 drawSlider( &painter, m_data->sliderRect );
691
692 if ( hasFocus() )
693 QwtPainter::drawFocusRect( &painter, this, m_data->sliderRect );
694}
695
700void QwtSlider::resizeEvent( QResizeEvent* event )
701{
702 layoutSlider( false );
703 QwtAbstractSlider::resizeEvent( event );
704}
705
712bool QwtSlider::event( QEvent* event )
713{
714 if ( event->type() == QEvent::PolishRequest )
715 layoutSlider( false );
716
717 return QwtAbstractSlider::event( event );
718}
719
724void QwtSlider::changeEvent( QEvent* event )
725{
726 if ( event->type() == QEvent::StyleChange ||
727 event->type() == QEvent::FontChange )
728 {
729 if ( testAttribute( Qt::WA_WState_Polished ) )
730 layoutSlider( true );
731 }
732
734}
735
743void QwtSlider::layoutSlider( bool update_geometry )
744{
745 int bw = 0;
746 if ( m_data->hasTrough )
747 bw = m_data->borderWidth;
748
749 const QSize handleSize = qwtHandleSize( m_data->handleSize,
750 m_data->orientation, m_data->hasTrough );
751
752 QRect sliderRect = contentsRect();
753
754 /*
755 The marker line of the handle needs to be aligned to
756 the scale. But the marker is in the center
757 and we need space enough to display the rest of the handle.
758
759 But the scale itself usually needs margins for displaying
760 the tick labels, that also might needs space beyond the
761 backbone.
762
763 Now it depends on what needs more margins. If it is the
764 slider the scale gets shrunk, otherwise the slider.
765 */
766
767 int scaleMargin = 0;
768 if ( m_data->scalePosition != QwtSlider::NoScale )
769 {
770 int d1, d2;
771 scaleDraw()->getBorderDistHint( font(), d1, d2 );
772
773 scaleMargin = qMax( d1, d2 ) - bw;
774 }
775
776 int scaleX, scaleY, scaleLength;
777
778 if ( m_data->orientation == Qt::Horizontal )
779 {
780 const int handleMargin = handleSize.width() / 2 - 1;
781 if ( scaleMargin > handleMargin )
782 {
783 int off = scaleMargin - handleMargin;
784 sliderRect.adjust( off, 0, -off, 0 );
785 }
786
787 scaleX = sliderRect.left() + bw + handleSize.width() / 2 - 1;
788 scaleLength = sliderRect.width() - handleSize.width();
789 }
790 else
791 {
792 int handleMargin = handleSize.height() / 2 - 1;
793 if ( scaleMargin > handleMargin )
794 {
795 int off = scaleMargin - handleMargin;
796 sliderRect.adjust( 0, off, 0, -off );
797 }
798
799 scaleY = sliderRect.top() + bw + handleSize.height() / 2 - 1;
800 scaleLength = sliderRect.height() - handleSize.height();
801 }
802
803 scaleLength -= 2 * bw;
804
805 // now align slider and scale according to the ScalePosition
806
807 if ( m_data->orientation == Qt::Horizontal )
808 {
809 const int h = handleSize.height() + 2 * bw;
810
811 if ( m_data->scalePosition == QwtSlider::TrailingScale )
812 {
813 sliderRect.setTop( sliderRect.bottom() + 1 - h );
814 scaleY = sliderRect.top() - m_data->spacing;
815 }
816 else
817 {
818 sliderRect.setHeight( h );
819 scaleY = sliderRect.bottom() + 1 + m_data->spacing;
820 }
821 }
822 else // Qt::Vertical
823 {
824 const int w = handleSize.width() + 2 * bw;
825
826 if ( m_data->scalePosition == QwtSlider::LeadingScale )
827 {
828 sliderRect.setWidth( w );
829 scaleX = sliderRect.right() + 1 + m_data->spacing;
830 }
831 else
832 {
833 sliderRect.setLeft( sliderRect.right() + 1 - w );
834 scaleX = sliderRect.left() - m_data->spacing;
835 }
836 }
837
838 m_data->sliderRect = sliderRect;
839
840 scaleDraw()->move( scaleX, scaleY );
841 scaleDraw()->setLength( scaleLength );
842
843 if ( update_geometry )
844 {
845 m_data->sizeHintCache = QSize(); // invalidate
846 updateGeometry();
847 update();
848 }
849}
850
860void QwtSlider::setTrough( bool on )
861{
862 if ( m_data->hasTrough != on )
863 {
864 m_data->hasTrough = on;
865
866 if ( testAttribute( Qt::WA_WState_Polished ) )
867 layoutSlider( true );
868 }
869}
870
876{
877 return m_data->hasTrough;
878}
879
889void QwtSlider::setGroove( bool on )
890{
891 if ( m_data->hasGroove != on )
892 {
893 m_data->hasGroove = on;
894
895 if ( testAttribute( Qt::WA_WState_Polished ) )
896 layoutSlider( true );
897 }
898}
899
905{
906 return m_data->hasGroove;
907}
908
913{
914 const QSize hint = minimumSizeHint();
915 return qwtExpandedToGlobalStrut( hint );
916}
917
923{
924 if ( !m_data->sizeHintCache.isEmpty() )
925 return m_data->sizeHintCache;
926
927 const QSize handleSize = qwtHandleSize( m_data->handleSize,
928 m_data->orientation, m_data->hasTrough );
929
930 int bw = 0;
931 if ( m_data->hasTrough )
932 bw = m_data->borderWidth;
933
934 int sliderLength = 0;
935 int scaleExtent = 0;
936
937 if ( m_data->scalePosition != QwtSlider::NoScale )
938 {
939 int d1, d2;
940 scaleDraw()->getBorderDistHint( font(), d1, d2 );
941
942 const int scaleBorderDist = 2 * ( qMax( d1, d2 ) - bw );
943
944 int handleBorderDist;
945 if ( m_data->orientation == Qt::Horizontal )
946 handleBorderDist = handleSize.width();
947 else
948 handleBorderDist = handleSize.height();
949
950 sliderLength = scaleDraw()->minLength( font() );
951 if ( handleBorderDist > scaleBorderDist )
952 {
953 // We need additional space for the overlapping handle
954 sliderLength += handleBorderDist - scaleBorderDist;
955 }
956
957 scaleExtent += m_data->spacing;
958 scaleExtent += qwtCeil( scaleDraw()->extent( font() ) );
959 }
960
961 sliderLength = qMax( sliderLength, 84 ); // from QSlider
962
963 int w = 0;
964 int h = 0;
965
966 if ( m_data->orientation == Qt::Horizontal )
967 {
968 w = sliderLength;
969 h = handleSize.height() + 2 * bw + scaleExtent;
970 }
971 else
972 {
973 w = handleSize.width() + 2 * bw + scaleExtent;
974 h = sliderLength;
975 }
976
977 // finally add margins
978 const QMargins m = contentsMargins();
979
980 w += m.left() + m.right();
981 h += m.top() + m.bottom();
982
983 m_data->sizeHintCache = QSize( w, h );
984 return m_data->sizeHintCache;
985}
986
991{
992 if ( !isValid() )
993 return QRect();
994
995 const int markerPos = transform( value() );
996
997 QPoint center = m_data->sliderRect.center();
998 if ( m_data->orientation == Qt::Horizontal )
999 center.setX( markerPos );
1000 else
1001 center.setY( markerPos );
1002
1003 QRect rect;
1004 rect.setSize( qwtHandleSize( m_data->handleSize,
1005 m_data->orientation, m_data->hasTrough ) );
1006 rect.moveCenter( center );
1007
1008 return rect;
1009}
1010
1015{
1016 return m_data->sliderRect;
1017}
1018
1019#include "moc_qwt_slider.cpp"
virtual void draw(QPainter *, const QPalette &) const
Draw the scale.
const QwtScaleMap & scaleMap() const
double lowerBound() const
const QwtAbstractScaleDraw * abstractScaleDraw() const
int transform(double) const
void setAbstractScaleDraw(QwtAbstractScaleDraw *)
Set a scale draw.
double upperBound() const
void setScale(double lowerBound, double upperBound)
Specify a scale.
virtual void changeEvent(QEvent *) override
An abstract base class for slider widgets with a scale.
virtual void scaleChange() override
virtual void mouseReleaseEvent(QMouseEvent *) override
virtual void mousePressEvent(QMouseEvent *) override
void valueChanged(double value)
Notify a change of value.
void setValue(double value)
void incrementValue(int stepCount)
double value() const
Returns the current value.
void sliderMoved(double value)
static void drawFocusRect(QPainter *, const QWidget *)
Draw a focus rectangle on a widget using its style.
A class for drawing scales.
void setLength(double length)
void move(double x, double y)
void getBorderDistHint(const QFont &, int &start, int &end) const
Determine the minimum border distance.
int minLength(const QFont &) const
void setAlignment(Alignment)
Alignment alignment() const
@ BottomScale
The scale is below.
@ TopScale
The scale is above.
@ RightScale
The scale is right.
@ LeftScale
The scale is left.
double invTransform(double p) const
The Slider Widget.
Definition qwt_slider.h:31
virtual void mouseReleaseEvent(QMouseEvent *) override
@ LeadingScale
The scale is right of a vertical or below a horizontal slider.
Definition qwt_slider.h:60
@ TrailingScale
The scale is left of a vertical or above a horizontal slider.
Definition qwt_slider.h:63
@ NoScale
The slider has no scale.
Definition qwt_slider.h:57
virtual void resizeEvent(QResizeEvent *) override
int spacing() const
virtual ~QwtSlider()
Destructor.
virtual bool isScrollPosition(const QPoint &) const override
Determine what to do when the user presses a mouse button.
bool hasGroove() const
bool hasTrough() const
void setOrientation(Qt::Orientation)
Set the orientation.
int borderWidth() const
QRect handleRect() const
const QwtScaleDraw * scaleDraw() const
void setGroove(bool)
void setScalePosition(ScalePosition)
Change the position of the scale.
virtual void paintEvent(QPaintEvent *) override
QwtSlider(QWidget *parent=NULL)
virtual bool event(QEvent *) override
virtual QSize minimumSizeHint() const override
virtual void drawHandle(QPainter *, const QRect &, int pos) const
void setHandleSize(const QSize &)
Set the slider's handle size.
virtual void timerEvent(QTimerEvent *) override
int updateInterval() const
void setScaleDraw(QwtScaleDraw *)
Set a scale draw.
void setSpacing(int)
Change the spacing between trough and scale.
virtual double scrolledTo(const QPoint &) const override
Determine the value for a new position of the slider handle.
virtual void changeEvent(QEvent *) override
ScalePosition scalePosition() const
QRect sliderRect() const
void setTrough(bool)
QSize handleSize() const
void setBorderWidth(int)
Change the slider's border width.
virtual void scaleChange() override
Notify changed scale.
virtual QSize sizeHint() const override
virtual void mousePressEvent(QMouseEvent *) override
void setUpdateInterval(int)
Specify the update interval for automatic scrolling.
Qt::Orientation orientation() const
virtual void drawSlider(QPainter *, const QRect &) const