QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskAbstractButton.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskAbstractButton.h"
7#include "QskAspect.h"
8#include "QskEvent.h"
9#include "QskQuick.h"
10
11#include <qbasictimer.h>
12
13QSK_SYSTEM_STATE( QskAbstractButton, Checked, QskAspect::LastSystemState >> 3 )
14QSK_SYSTEM_STATE( QskAbstractButton, Pressed, QskAspect::LastSystemState >> 2 )
15
16static QskAbstractButton* qskCheckedSibling( const QskAbstractButton * button )
17{
18 const auto parentItem = button->parentItem();
19 if ( parentItem == nullptr )
20 return nullptr;
21
22 const auto siblings = parentItem->childItems();
23 for ( auto sibling : siblings )
24 {
25 if ( auto btn = qobject_cast< QskAbstractButton* >( sibling ) )
26 {
27 if ( btn != button && btn->exclusive() && btn->isChecked() )
28 return btn;
29 }
30 }
31
32 return nullptr;
33}
34
35class QskAbstractButton::PrivateData
36{
37 public:
38 PrivateData()
39 : autoRepeatDelay( 300 )
40 , autoRepeatInterval( 100 )
41 , exclusive( false )
42 , autoRepeat( false )
43 {
44 }
45
46 QBasicTimer repeatTimer;
47
48 int autoRepeatDelay; // milliseconds
49 int autoRepeatInterval; // milliseconds
50
51 bool exclusive : 1;
52 bool autoRepeat : 1;
53};
54
55QskAbstractButton::QskAbstractButton( QQuickItem* parent )
56 : Inherited( parent )
57 , m_data( new PrivateData() )
58{
59 setFocusPolicy( Qt::StrongFocus );
60 setAcceptedMouseButtons( Qt::LeftButton );
61 setAcceptHoverEvents( true );
62}
63
64QskAbstractButton::~QskAbstractButton()
65{
66}
67
68void QskAbstractButton::click()
69{
70 setPressed( true );
71 releaseButton();
72}
73
74void QskAbstractButton::releaseButton()
75{
76 if ( !isPressed() )
77 return;
78
79 if ( isCheckable() )
80 {
81 // we will have toggled before released,
82 // maybe there is more work to have the signals coming
83 // in a logical order. TODO ...
84
85 setCheckedState( !hasSkinState( Checked ) );
86 }
87
88 setPressed( false );
89 Q_EMIT clicked();
90}
91
92void QskAbstractButton::setCheckedState( bool on )
93{
94 setChecked( on );
95}
96
97void QskAbstractButton::toggle()
98{
99 setChecked( !isChecked() );
100}
101
102bool QskAbstractButton::isPressed() const
103{
104 return hasSkinState( Pressed );
105}
106
107void QskAbstractButton::setPressed( bool on )
108{
109 if ( on == isPressed() )
110 return;
111
112 setSkinStateFlag( Pressed, on );
113 Q_EMIT pressedChanged( on );
114
115 if ( on )
116 Q_EMIT pressed();
117 else
118 Q_EMIT released();
119
120 if ( m_data->autoRepeat )
121 {
122 if ( on )
123 {
124 m_data->repeatTimer.start( m_data->autoRepeatDelay, this );
125 }
126 else
127 {
128 m_data->repeatTimer.stop();
129 }
130 }
131}
132
133bool QskAbstractButton::isCheckable() const
134{
135 return false;
136}
137
138void QskAbstractButton::setChecked( bool on )
139{
140 if ( on == isChecked() )
141 return;
142
143 if ( m_data->exclusive && !on )
144 {
145 // an exclusive button can't be unchecked
146 return;
147 }
148
149 auto checkedButton = qskCheckedSibling( this );
150 if ( checkedButton )
151 {
152 // we enter temporary state, where no buttons are selected,
153 // better delay the notifications
154
155 checkedButton->setSkinStateFlag( Checked, false );
156 }
157
158 setSkinStateFlag( Checked, on );
159 Q_EMIT checkedChanged( on );
160 Q_EMIT toggled( on );
161
162 if ( checkedButton )
163 {
164 Q_EMIT checkedButton->checkedChanged( false );
165 Q_EMIT checkedButton->toggled( false );
166 }
167}
168
169bool QskAbstractButton::isChecked() const
170{
171 return hasSkinState( Checked );
172}
173
174void QskAbstractButton::setAutoRepeat( bool on )
175{
176 if ( on != m_data->autoRepeat )
177 {
178 m_data->autoRepeat = on;
179
180 if ( m_data->autoRepeat && isPressed() )
181 m_data->repeatTimer.start( m_data->autoRepeatDelay, this );
182 else
183 m_data->repeatTimer.stop();
184
185 Q_EMIT autoRepeatChanged( on );
186 }
187}
188
189bool QskAbstractButton::autoRepeat() const
190{
191 return m_data->autoRepeat;
192}
193
194void QskAbstractButton::setAutoRepeatDelay( int ms )
195{
196 if ( ms != m_data->autoRepeatDelay )
197 {
198 m_data->autoRepeatDelay = ms;
199 Q_EMIT autoRepeatDelayChanged();
200 }
201}
202
203int QskAbstractButton::autoRepeatDelay() const
204{
205 return m_data->autoRepeatDelay;
206}
207
208void QskAbstractButton::setAutoRepeatInterval( int ms )
209{
210 if ( ms != m_data->autoRepeatInterval )
211 {
212 m_data->autoRepeatInterval = ms;
213 Q_EMIT autoRepeatIntervalChanged();
214 }
215}
216
217int QskAbstractButton::autoRepeatInterval() const
218{
219 return m_data->autoRepeatInterval;
220}
221
222void QskAbstractButton::setExclusive( bool on )
223{
224 if ( on != m_data->exclusive )
225 {
226 m_data->exclusive = on;
227 Q_EMIT exclusiveChanged( on );
228 }
229}
230
231bool QskAbstractButton::exclusive() const
232{
233 return m_data->exclusive;
234}
235
236bool QskAbstractButton::event( QEvent* event )
237{
238 const auto eventType = static_cast< int >( event->type() );
239 switch ( eventType )
240 {
241 case QEvent::Shortcut:
242 {
243 // TODO
244 // setFocus( true, Qt::ShortcutFocusReason );
245 break;
246 }
247 case QskEvent::WindowChange:
248 {
249 if ( qskIsItemComplete( this ) && isPressed() )
250 {
251 /*
252 When the window change happens on pressed() we won't get
253 the corrsponding release.
254 */
255 setPressed( false );
256 }
257
258 break;
259 }
260 }
261
262 return Inherited::event( event );
263}
264
265void QskAbstractButton::keyPressEvent( QKeyEvent* event )
266{
267 if ( qskIsButtonPressKey( event ) )
268 {
269 if ( !event->isAutoRepeat() )
270 setPressed( true );
271
272 // always accepting
273 return;
274 }
275
276 Inherited::keyPressEvent( event );
277}
278
279void QskAbstractButton::keyReleaseEvent( QKeyEvent* event )
280{
281 if ( qskIsButtonPressKey( event ) )
282 {
283 releaseButton();
284 return;
285 }
286
287 Inherited::keyReleaseEvent( event );
288}
289
290void QskAbstractButton::mouseMoveEvent( QMouseEvent* event )
291{
292 if ( isPressed() )
293 {
294 if ( !contains( event->pos() ) )
295 {
296 setPressed( false );
297 Q_EMIT canceled();
298 }
299 }
300}
301
302void QskAbstractButton::mousePressEvent( QMouseEvent* )
303{
304 // QGuiApplication::styleHints()->mousePressAndHoldInterval() ???
305 setPressed( true );
306}
307
308void QskAbstractButton::mouseReleaseEvent( QMouseEvent* )
309{
310 releaseButton();
311}
312
313void QskAbstractButton::mouseUngrabEvent()
314{
315 if ( isPressed() )
316 {
317 setPressed( false );
318 Q_EMIT canceled();
319 }
320}
321
322void QskAbstractButton::focusInEvent( QFocusEvent* event )
323{
324 Inherited::focusInEvent( event );
325}
326
327void QskAbstractButton::focusOutEvent( QFocusEvent* event )
328{
329 if ( isPressed() )
330 {
331 setPressed( false );
332 Q_EMIT canceled();
333 }
334
335 Inherited::focusOutEvent( event );
336}
337
338void QskAbstractButton::timerEvent( QTimerEvent* event )
339{
340 if ( event->timerId() == m_data->repeatTimer.timerId() )
341 {
342 if ( isPressed() )
343 {
344 Q_EMIT released();
345 Q_EMIT clicked();
346 Q_EMIT pressed();
347
348 m_data->repeatTimer.start( m_data->autoRepeatInterval, this );
349 }
350 else
351 {
352 m_data->repeatTimer.stop();
353 }
354
355 return;
356 }
357
358 Inherited::timerEvent( event );
359}
360
361#include "moc_QskAbstractButton.cpp"
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
@ LastSystemState
Definition QskAspect.h:118
void setSkinStateFlag(QskAspect::State, bool on=true)