QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSlider.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSlider.h"
7#include "QskAnimationHint.h"
8#include "QskAspect.h"
9#include "QskIntervalF.h"
10#include "QskEvent.h"
11
12QSK_SUBCONTROL( QskSlider, Panel )
13QSK_SUBCONTROL( QskSlider, Groove )
14QSK_SUBCONTROL( QskSlider, Fill )
15QSK_SUBCONTROL( QskSlider, Scale )
16QSK_SUBCONTROL( QskSlider, Tick )
17QSK_SUBCONTROL( QskSlider, Handle )
18
19QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 )
20
21static inline QskAspect qskAspectGraduationPolicy()
22{
23 return QskSlider::Tick | QskAspect::Option;
24}
25
26class QskSlider::PrivateData
27{
28 public:
29 PrivateData( Qt::Orientation orientation )
30 : pressedValue( 0 )
31 , tracking( true )
32 , orientation( orientation )
33 {
34 }
35
36 QPointF pressedPos;
37 qreal pressedValue;
38 bool tracking : 1;
39 uint orientation : 2;
40};
41
42QskSlider::QskSlider( QQuickItem* parent )
43 : QskSlider( Qt::Horizontal, parent )
44{
45}
46
47QskSlider::QskSlider( Qt::Orientation orientation, QQuickItem* parent )
48 : Inherited( parent )
49 , m_data( new PrivateData( orientation ) )
50{
51 setAcceptHoverEvents( true );
52 setFocusPolicy( Qt::StrongFocus );
53
54 if ( orientation == Qt::Horizontal )
55 initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
56 else
57 initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Minimum );
58
59 connect( this, &QskSlider::boundariesChanged, this, &QskSlider::moveHandle );
60 connect( this, &QskSlider::valueChanged, this, &QskSlider::moveHandle );
61}
62
63QskSlider::~QskSlider()
64{
65}
66
67bool QskSlider::isPressed() const
68{
69 return hasSkinState( Pressed );
70}
71
72void QskSlider::setOrientation( Qt::Orientation orientation )
73{
74 if ( orientation != m_data->orientation )
75 {
76 m_data->orientation = orientation;
77#if 1
78 // swapping the size policy: guess this is what a user expects
79 setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) );
80#endif
82 update();
83
84 Q_EMIT orientationChanged( this->orientation() );
85 }
86}
87
88Qt::Orientation QskSlider::orientation() const
89{
90 return static_cast< Qt::Orientation >( m_data->orientation );
91}
92
94{
95 return static_cast< QskAspect::Variation >( m_data->orientation );
96}
97
98void QskSlider::setGraduationPolicy( Qsk::Policy policy )
99{
100 if ( setFlagHint( qskAspectGraduationPolicy(), policy ) )
101 Q_EMIT graduationPolicyChanged( graduationPolicy() );
102}
103
104void QskSlider::resetGraduationPolicy()
105{
106 if ( resetSkinHint( qskAspectGraduationPolicy() ) )
107 Q_EMIT graduationPolicyChanged( graduationPolicy() );
108}
109
110Qsk::Policy QskSlider::graduationPolicy() const
111{
112 return flagHint< Qsk::Policy >( qskAspectGraduationPolicy(), Qsk::Never );
113}
114
115void QskSlider::setTracking( bool on )
116{
117 if ( on != m_data->tracking )
118 {
119 m_data->tracking = on;
120 Q_EMIT trackingChanged( on );
121 }
122}
123
124bool QskSlider::isTracking() const
125{
126 return m_data->tracking;
127}
128
130{
131 setPositionHint( Handle, valueAsRatio() );
133}
134
135QSizeF QskSlider::handleSize() const
136{
137 return handleRect().size();
138}
139
140QRectF QskSlider::handleRect() const
141{
142 auto rect = subControlRect( Handle );
143
144#if 1 // minimum handle strut size hardcoded here for now
145 const QSizeF strutSize( 60, 60 );
146 const auto w = qMax( ( strutSize.width() - rect.width() ) / 2, 0.0 );
147 const auto h = qMax( ( strutSize.height() - rect.height() ) / 2, 0.0 );
148#endif
149
150 return rect.marginsAdded( { w, h, w, h } );
151}
152
153void QskSlider::mousePressEvent( QMouseEvent* event )
154{
155 if ( handleRect().contains( event->pos() ) )
156 {
157 // Case 1: press started in the handle, start sliding
158
159 m_data->pressedPos = event->pos();
160 m_data->pressedValue = value();
161 setSkinStateFlag( Pressed );
162 Q_EMIT pressedChanged( true );
163 }
164 else if ( pageSteps() == 0 )
165 {
166 // Case 2: pageSize is not used, we're done here
167 }
168 else
169 {
170 // Case 3: pressed outside of the handle, page the scroller in
171 // the direction of the press requires an auto-repeat behavior
172 // until the slider reaches the destination, or it simply jumps
173 // there (configurable)
174 }
175}
176
177void QskSlider::mouseMoveEvent( QMouseEvent* event )
178{
179 if ( !isPressed() )
180 return;
181
182 const auto mousePos = qskMousePosition( event );
183 const auto r = subControlRect( Scale );
184
185 qreal newValue;
186
187 if ( m_data->orientation == Qt::Horizontal )
188 {
189 const auto distance = mousePos.x() - m_data->pressedPos.x();
190 newValue = m_data->pressedValue + distance / r.width() * boundaryLength();
191 }
192 else
193 {
194 const auto distance = mousePos.y() - m_data->pressedPos.y();
195 newValue = m_data->pressedValue - distance / r.height() * boundaryLength();
196 }
197
198 if ( m_data->tracking )
199 {
200 setValue( newValue );
201 }
202 else
203 {
204 moveHandleTo( newValue, QskAnimationHint() );
205 }
206}
207
208void QskSlider::mouseReleaseEvent( QMouseEvent* event )
209{
210 if ( !isPressed() ) // Page event
211 {
212 const auto mousePos = qskMousePosition( event );
213
214 const auto szHandle = handleSize();
215 const auto rect = contentsRect();
216
217 bool up;
218 if ( m_data->orientation == Qt::Horizontal )
219 {
220 const qreal w = szHandle.width();
221
222 const qreal x = ( mousePos.x() - rect.x() - w * 0.5 ) / ( rect.width() - w );
223 up = x > valueAsRatio();
224 }
225 else
226 {
227 const qreal h = szHandle.height();
228
229 const qreal y = ( mousePos.y() - rect.y() - h * 0.5 ) / ( rect.height() - h );
230 up = y < 1.0 - valueAsRatio();
231 }
232
233 if ( up )
234 pageUp();
235 else
236 pageDown();
237 }
238 else
239 {
240 if ( !m_data->tracking )
241 {
242 const auto pos = handlePosition();
243 setValue( valueFromRatio( pos ) );
244 }
245 }
246
247 setSkinStateFlag( Pressed, false );
248 Q_EMIT pressedChanged( false );
249}
250
251qreal QskSlider::handlePosition() const
252{
253 return positionHint( Handle );
254}
255
256void QskSlider::moveHandle()
257{
258 const auto aspect = Handle | QskAspect::Metric | QskAspect::Position;
259 moveHandleTo( value(), animationHint( aspect | skinStates() ) );
260}
261
262void QskSlider::moveHandleTo( qreal value, const QskAnimationHint& hint )
263{
264 const qreal pos = valueAsRatio( value );
265
266 if ( hint.isValid() )
267 {
268 const qreal oldPos = positionHint( Handle );
269 setPositionHint( Handle, pos );
270
271 const auto aspect = Handle | QskAspect::Metric | QskAspect::Position;
272 startTransition( aspect, hint, oldPos, pos );
273 }
274 else
275 {
276 setPositionHint( Handle, pos );
277 }
278
279 update();
280}
281
282#include "moc_QskSlider.cpp"
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
@ FirstSystemState
Definition QskAspect.h:115
Variation
Some sort of variation.
Definition QskAspect.h:82
QRectF subControlRect(QskAspect::Subcontrol) const
void setSizePolicy(QskSizePolicy)
QskSizePolicy sizePolicy
Definition QskControl.h:43
QRectF contentsRect() const
QRectF rect
Definition QskItem.h:21
void resetImplicitSize()
Definition QskItem.cpp:721
virtual void aboutToShow()
Definition QskItem.cpp:1091
bool resetSkinHint(QskAspect)
Remove a hint from the local hint table.
QskAnimationHint animationHint(QskAspect, QskSkinHintStatus *=nullptr) const
void setSkinStateFlag(QskAspect::State, bool on=true)
bool setFlagHint(QskAspect, int flag)
Sets a flag hint.
QskAspect::Variation effectiveVariation() const override
Definition QskSlider.cpp:93
void aboutToShow() override