QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSpinBox.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSpinBox.h"
7#include "QskEvent.h"
8#include "QskFunctions.h"
9
10#include <qbasictimer.h>
11
12QSK_SUBCONTROL( QskSpinBox, Panel )
13
14QSK_SUBCONTROL( QskSpinBox, TextPanel )
15QSK_SUBCONTROL( QskSpinBox, Text )
16
17QSK_SUBCONTROL( QskSpinBox, UpPanel )
18QSK_SUBCONTROL( QskSpinBox, UpIndicator )
19
20QSK_SUBCONTROL( QskSpinBox, DownPanel )
21QSK_SUBCONTROL( QskSpinBox, DownIndicator )
22
23QSK_SYSTEM_STATE( QskSpinBox, Increasing, ( QskAspect::FirstSystemState << 2 ) )
24QSK_SYSTEM_STATE( QskSpinBox, Decreasing, ( QskAspect::FirstSystemState << 3 ) )
25
26namespace
27{
28 inline QskAspect::Subcontrol buttonAt( const QskSpinBox* spinBox, const QPointF& pos )
29 {
30 if ( spinBox->subControlRect( QskSpinBox::UpPanel ).contains( pos ) )
31 return QskSpinBox::UpPanel;
32
33 if ( spinBox->subControlRect( QskSpinBox::DownPanel ).contains( pos ) )
34 return QskSpinBox::DownPanel;
35
36 return QskAspect::NoSubcontrol;
37 }
38
39 inline QskAspect aspectDecoration()
40 {
41 return QskSpinBox::Panel | QskAspect::Style;
42 }
43}
44
45class QskSpinBox::PrivateData
46{
47 public:
48 PrivateData()
49 : wrapping( false )
50 {
51 }
52
53 inline bool isActivatedByMouse() const
54 {
55 return this->repeatTimer.isActive() && ( this->key == Qt::Key_unknown );
56 }
57
58 inline bool isTimerBlocked( QskSpinBox* spinBox, qreal offset ) const
59 {
60 if ( !this->wrapping )
61 {
62 if ( offset >= 0.0 )
63 return spinBox->value() >= spinBox->maximum();
64 else
65 return spinBox->value() <= spinBox->minimum();
66 }
67
68 return false;
69 }
70
71 inline void setAutoRepeat( QskSpinBox* spinBox, qreal offset )
72 {
73 this->autoRepeatIncrement = offset;
74 this->key = Qt::Key_unknown;
75
76 if( offset < 0.0 )
77 {
78 if ( spinBox->hasSkinState( QskSpinBox::Increasing ) )
79 spinBox->setSkinStateFlag( QskSpinBox::Increasing, false );
80
81 if ( !spinBox->hasSkinState( QskSpinBox::Decreasing ) )
82 {
83 spinBox->setSkinStateFlag( QskSpinBox::Decreasing, true );
84 this->repeatTimer.start( autoRepeatDelay, spinBox );
85 }
86
87 return;
88 }
89
90 if ( offset > 0.0 )
91 {
92 if ( spinBox->hasSkinState( QskSpinBox::Decreasing ) )
93 spinBox->setSkinStateFlag( QskSpinBox::Decreasing, false );
94
95 if ( !spinBox->hasSkinState( QskSpinBox::Increasing ) )
96 {
97 spinBox->setSkinStateFlag( QskSpinBox::Increasing, true );
98 this->repeatTimer.start( autoRepeatDelay, spinBox );
99 }
100
101 return;
102 }
103
104 spinBox->setSkinStateFlag( QskSpinBox::Decreasing, false );
105 spinBox->setSkinStateFlag( QskSpinBox::Increasing, false );
106
107 this->repeatTimer.stop();
108 }
109
110 int autoRepeatDelay = 300;
111 int autoRepeatInterval = 100;
112
113 qreal autoRepeatIncrement = 0.0;
114
115 QBasicTimer repeatTimer;
116
117 int key = Qt::Key_unknown;
118
119 bool wrapping : 1;
120
121#if 0
122 // not yet implemented: TODO ...
123 bool tracking : 1;
124 bool accelerating : 1;
125#endif
126};
127
128QskSpinBox::QskSpinBox( qreal min, qreal max, qreal stepSize, QQuickItem* parent )
129 : Inherited( parent )
130 , m_data( new PrivateData )
131{
132 initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
133
134 setBoundaries( min, max );
135 setStepSize( stepSize );
136
137 setAcceptHoverEvents( true );
138
139 setAcceptedMouseButtons( Qt::LeftButton );
140 setFocusPolicy( Qt::StrongFocus );
141}
142
143QskSpinBox::QskSpinBox( QQuickItem* parent )
144 : QskSpinBox( 0.0, 99.99, 0.1, parent )
145{
146}
147
148QskSpinBox::~QskSpinBox()
149{
150}
151
152void QskSpinBox::setDecoration( Decoration decoration )
153{
154 if ( setFlagHint( aspectDecoration(), decoration ) )
155 Q_EMIT decorationChanged( decoration );
156}
157
158void QskSpinBox::resetDecoration()
159{
160 if ( resetSkinHint( aspectDecoration() ) )
161 Q_EMIT decorationChanged( decoration() );
162}
163
164QskSpinBox::Decoration QskSpinBox::decoration() const
165{
166 return flagHint< QskSpinBox::Decoration >( aspectDecoration(), ButtonsLeftAndRight );
167}
168
169void QskSpinBox::setWrapping( bool on )
170{
171 if ( on != m_data->wrapping )
172 {
173 m_data->wrapping = on;
174 Q_EMIT wrappingChanged( on );
175 }
176}
177
178bool QskSpinBox::isWrapping() const
179{
180 return m_data->wrapping;
181}
182
183void QskSpinBox::increment( qreal offset )
184{
185 if ( m_data->wrapping )
186 {
187 const auto v = value();
188
189 if ( offset > 0.0 && qskFuzzyCompare( v, maximum() ) )
190 {
191 setValue( minimum() );
192 return;
193 }
194
195 if ( offset < 0.0 && qskFuzzyCompare( v, minimum() ) )
196 {
197 setValue( maximum() );
198 return;
199 }
200 }
201
202 Inherited::increment( offset );
203}
204
205void QskSpinBox::mousePressEvent( QMouseEvent* event )
206{
207 if ( !isReadOnly() )
208 {
209 if ( auto subcontrol = buttonAt( this, qskMousePosition( event ) ) )
210 {
211 bool acceptPress = !m_data->repeatTimer.isActive();
212
213 if ( acceptPress && !m_data->wrapping )
214 {
215 if ( subcontrol == QskSpinBox::DownPanel )
216 acceptPress = value() > minimum();
217 else
218 acceptPress = value() < maximum();
219 }
220
221 if ( acceptPress )
222 {
223 auto offset = stepSize();
224 if ( event->modifiers() & ( Qt::ControlModifier | Qt::ShiftModifier ) )
225 offset *= pageSteps();
226
227 if ( subcontrol == QskSpinBox::DownPanel )
228 offset = -offset;
229
230 increment( offset );
231
232 if ( !m_data->isTimerBlocked( this, offset ) )
233 m_data->setAutoRepeat( this, offset );
234 }
235 return;
236 }
237 }
238
239 Inherited::mousePressEvent( event );
240}
241
242void QskSpinBox::mouseReleaseEvent( QMouseEvent* )
243{
244 if ( m_data->isActivatedByMouse() )
245 m_data->setAutoRepeat( this, 0.0 );
246}
247
248void QskSpinBox::mouseMoveEvent( QMouseEvent* event )
249{
250 if ( m_data->isActivatedByMouse() )
251 {
252 const auto subcontrol = buttonAt( this, qskMousePosition( event ) );
253
254 if ( m_data->autoRepeatIncrement >= 0.0 )
255 {
256 if ( subcontrol != QskSpinBox::UpPanel )
257 m_data->setAutoRepeat( this, 0.0 );
258 }
259 else
260 {
261 if ( subcontrol != QskSpinBox::DownPanel )
262 m_data->setAutoRepeat( this, 0.0 );
263 }
264 }
265}
266
267void QskSpinBox::mouseUngrabEvent()
268{
269 if ( m_data->isActivatedByMouse() )
270 m_data->setAutoRepeat( this, 0.0 );
271}
272
273void QskSpinBox::hoverMoveEvent( QHoverEvent* event )
274{
275 Inherited::hoverMoveEvent( event );
276 update(); // enter/leaving a subcontrol
277}
278
279void QskSpinBox::keyPressEvent( QKeyEvent* event )
280{
281 if ( !isReadOnly() && !m_data->repeatTimer.isActive() )
282 {
283 const auto offset = incrementForKey( event );
284 if ( offset != 0.0 )
285 {
286 if ( !m_data->repeatTimer.isActive() )
287 {
288 increment( offset );
289
290 if ( !m_data->isTimerBlocked( this, offset ) )
291 {
292 m_data->setAutoRepeat( this, offset );
293 m_data->key = event->key();
294 }
295 }
296 return;
297 }
298 }
299
300 Inherited::keyPressEvent( event );
301}
302
303void QskSpinBox::keyReleaseEvent( QKeyEvent* event )
304{
305 if ( m_data->key == event->key() )
306 {
307 m_data->setAutoRepeat( this, 0.0 );
308 return;
309 }
310
311 Inherited::keyReleaseEvent( event );
312}
313
314void QskSpinBox::timerEvent( QTimerEvent* event )
315{
316 if ( event->timerId() == m_data->repeatTimer.timerId() )
317 {
318 if ( skinStates() & ( QskSpinBox::Increasing | QskSpinBox::Decreasing ) )
319 {
320 increment( m_data->autoRepeatIncrement );
321
322 if ( m_data->isTimerBlocked( this, m_data->autoRepeatIncrement ) )
323 m_data->setAutoRepeat( this, 0.0 );
324 else
325 m_data->repeatTimer.start( m_data->autoRepeatInterval, this );
326 }
327
328 return;
329 }
330
331 Inherited::timerEvent( event );
332}
333
334#include "moc_QskSpinBox.cpp"
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
@ FirstSystemState
Definition QskAspect.h:115
Subcontrol
For use within the rendering or lay-outing of a specific QskSkinnable.
Definition QskAspect.h:104
QRectF subControlRect(QskAspect::Subcontrol) const
bool resetSkinHint(QskAspect)
Remove a hint from the local hint table.
void setSkinStateFlag(QskAspect::State, bool on=true)
bool setFlagHint(QskAspect, int flag)
Sets a flag hint.
A control to edit, increment and decrement number values.
Definition QskSpinBox.h:12
QskSpinBox(QQuickItem *parent=nullptr)