|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbCore module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include "hbabstractbutton.h" |
|
27 #include "hbabstractbutton_p.h" |
|
28 #include "hbapplication.h" |
|
29 #include "hbstyleoption.h" |
|
30 #include "hbtooltip.h" |
|
31 #include "hbinstance.h" |
|
32 #include <QGraphicsSceneMouseEvent> |
|
33 #include <QGraphicsScene> |
|
34 #include <QPointer> |
|
35 #include <QStyle> |
|
36 |
|
37 #include <hbwidgetfeedback.h> |
|
38 |
|
39 namespace { |
|
40 static const int AUTO_REPEAT_DELAY = 300; |
|
41 static const int AUTO_REPEAT_INTERVAL = 100; |
|
42 } |
|
43 |
|
44 /*! |
|
45 @beta |
|
46 @hbcore |
|
47 \class HbAbstractButton |
|
48 |
|
49 \brief The HbAbstractButton class is the abstract base class of |
|
50 button widgets, providing functionality common to buttons. |
|
51 |
|
52 This class implements an \e abstract button. |
|
53 Subclasses of this class handle user actions, and specify how the button |
|
54 is drawn. |
|
55 |
|
56 HbAbstractButton provides support for both push buttons and checkable |
|
57 (toggle) buttons. Checkable buttons are implemented in the HbRadioButton |
|
58 and HbCheckBox classes. Push buttons are implemented in the |
|
59 HbPushButton and HbToolButton classes; these also provide toggle |
|
60 behavior if required. |
|
61 |
|
62 Any button can display text and an icon. setText() |
|
63 sets the text; setIcon() sets the icon. If a button is disabled, its background |
|
64 is changed to give the button a "disabled" appearance. |
|
65 |
|
66 All of the buttons provided by Hb (HbPushButton, HbToolButton, |
|
67 HbCheckBox, and HbRadioButton) can display both text and icon. |
|
68 You can also set a tool tip for the button with \qtfunc{QGraphicsItem,setTooltip}. |
|
69 |
|
70 HbAbstractButton provides most of the states used for buttons: |
|
71 |
|
72 \li isDown() indicates whether the button is \e pressed down. |
|
73 |
|
74 \li isChecked() indicates whether the button is \e checked. Only |
|
75 checkable buttons can be checked and unchecked (see below). |
|
76 |
|
77 \li isEnabled() indicates whether the button can be pressed by the |
|
78 user. |
|
79 |
|
80 \li setAutoRepeat() sets whether the button will auto-repeat if the |
|
81 user holds it down. \l autoRepeatDelay and \l autoRepeatInterval |
|
82 define how auto-repetition is done. |
|
83 |
|
84 \li setCheckable() sets whether the button is a toggle button or not. |
|
85 |
|
86 The difference between isDown() and isChecked() is as follows. |
|
87 When the user clicks a toggle button to check it, the button is first |
|
88 \e pressed then released into the \e checked state. When the user |
|
89 clicks it again (to uncheck it), the button moves first to the |
|
90 \e pressed state, then to the \e unchecked state (isChecked() and |
|
91 isDown() are both false). |
|
92 |
|
93 HbAbstractButton provides four signals: |
|
94 |
|
95 \li pressed() is emitted when the stylus is pressed while |
|
96 the stylus is inside the button. |
|
97 |
|
98 \li released() is emitted when the left stylus is released. |
|
99 |
|
100 \li clicked() is emitted when the button is first pressed and then |
|
101 released, when the shortcut key is typed, or when click() or |
|
102 animateClick() is called. |
|
103 |
|
104 \li toggled() is emitted when the state of a toggle button changes. |
|
105 |
|
106 To subclass HbAbstractButton, you must reimplement at least |
|
107 paint() to draw the button's outline and its text or pixmap. It |
|
108 is generally advisable to reimplement sizeHint() as well, and |
|
109 sometimes hitButton() (to determine whether a button press is within |
|
110 the button). For buttons with more than two states (like tri-state |
|
111 buttons), you will also have to reimplement checkStateSet() and |
|
112 nextCheckState(). |
|
113 */ |
|
114 |
|
115 /*! |
|
116 \reimp |
|
117 \fn int HbAbstractButton::type() const |
|
118 */ |
|
119 |
|
120 HbAbstractButtonPrivate::HbAbstractButtonPrivate( QSizePolicy::ControlType type ) |
|
121 : |
|
122 shortcutId(0), |
|
123 checkable(false), checked(false), autoRepeat(false), autoExclusive(false), |
|
124 down(false), blockRefresh(false),longPress(false), |
|
125 autoRepeatDelay(AUTO_REPEAT_DELAY), |
|
126 autoRepeatInterval(AUTO_REPEAT_INTERVAL), |
|
127 controlType(type), sizeHint(),mSizeHintPolish(false),mRepolishRequested(false) |
|
128 { |
|
129 } |
|
130 |
|
131 HbAbstractButtonPrivate::~HbAbstractButtonPrivate() |
|
132 { |
|
133 } |
|
134 |
|
135 QList<HbAbstractButton *>HbAbstractButtonPrivate::queryButtonList() const |
|
136 { |
|
137 Q_Q( const HbAbstractButton ); |
|
138 |
|
139 QList<HbAbstractButton*>candidates; |
|
140 if (q->parentWidget()) { |
|
141 candidates = qFindChildren<HbAbstractButton *>(q->parentWidget()); |
|
142 if (autoExclusive) { |
|
143 for (int i = candidates.count() - 1; i >= 0; --i) { |
|
144 HbAbstractButton *candidate = candidates.at(i); |
|
145 if (!candidate->autoExclusive()) |
|
146 candidates.removeAt(i); |
|
147 } |
|
148 } |
|
149 } |
|
150 return candidates; |
|
151 } |
|
152 |
|
153 HbAbstractButton *HbAbstractButtonPrivate::queryCheckedButton() const |
|
154 { |
|
155 QList<HbAbstractButton *> buttonList = queryButtonList(); |
|
156 if (!autoExclusive || buttonList.count() == 1) // no group |
|
157 return 0; |
|
158 |
|
159 Q_Q( const HbAbstractButton ); |
|
160 |
|
161 for (int i = 0; i < buttonList.count(); ++i) { |
|
162 HbAbstractButton *b = buttonList.at(i); |
|
163 if (b->isChecked() && b != q) |
|
164 return b; |
|
165 } |
|
166 return checked ? const_cast<HbAbstractButton *>(q) : 0; |
|
167 } |
|
168 |
|
169 void HbAbstractButtonPrivate::notifyChecked() |
|
170 { |
|
171 if (autoExclusive) { |
|
172 if (HbAbstractButton *b = queryCheckedButton()) |
|
173 b->setChecked(false); |
|
174 } |
|
175 } |
|
176 |
|
177 void HbAbstractButtonPrivate::moveFocus(int key) |
|
178 { |
|
179 Q_Q( HbAbstractButton ); |
|
180 |
|
181 QList<HbAbstractButton *> buttonList = queryButtonList();; |
|
182 bool exclusive = autoExclusive; |
|
183 QGraphicsItem *focusItem = q->scene()->focusItem(); |
|
184 HbAbstractButton *focusButton = 0; |
|
185 if (focusItem && focusItem->isWidget()) { |
|
186 QGraphicsWidget *focusWidget = static_cast<QGraphicsWidget*>(focusItem); |
|
187 focusButton = qobject_cast<HbAbstractButton *>(focusWidget); |
|
188 } |
|
189 if (!focusButton || !buttonList.contains(focusButton)) |
|
190 return; |
|
191 |
|
192 HbAbstractButton *candidate = 0; |
|
193 int bestScore = -1; |
|
194 QRect target = focusItem->sceneBoundingRect().toRect(); |
|
195 QPoint goal = target.center(); |
|
196 |
|
197 for (int i = 0; i < buttonList.count(); ++i) { |
|
198 HbAbstractButton *button = buttonList.at(i); |
|
199 if (button != focusItem && button->isEnabled() && button->isVisible() && |
|
200 (autoExclusive || (button->focusPolicy() & Qt::StrongFocus) == Qt::StrongFocus)) { |
|
201 QRect buttonRect = button->sceneBoundingRect().toRect(); |
|
202 QPoint p = buttonRect.center(); |
|
203 |
|
204 //Priority to widgets that overlap on the same coordinate. |
|
205 //In that case, the distance in the direction will be used as significant score, |
|
206 //take also in account orthogonal distance in case two widget are in the same distance. |
|
207 int score; |
|
208 if ((buttonRect.x() < target.right() && target.x() < buttonRect.right()) |
|
209 && (key == Qt::Key_Up || key == Qt::Key_Down)) { |
|
210 //one item's is at the vertical of the other |
|
211 score = (qAbs(p.y() - goal.y()) << 16) + qAbs(p.x() - goal.x()); |
|
212 } else if ((buttonRect.y() < target.bottom() && target.y() < buttonRect.bottom()) |
|
213 && (key == Qt::Key_Left || key == Qt::Key_Right) ) { |
|
214 //one item's is at the horizontal of the other |
|
215 score = (qAbs(p.x() - goal.x()) << 16) + qAbs(p.y() - goal.y()); |
|
216 } else { |
|
217 score = (1 << 30) + (p.y() - goal.y()) * (p.y() - goal.y()) + (p.x() - goal.x()) * (p.x() - goal.x()); |
|
218 } |
|
219 |
|
220 if (score > bestScore && candidate) |
|
221 continue; |
|
222 |
|
223 switch(key) { |
|
224 case Qt::Key_Up: |
|
225 if (p.y() < goal.y()) { |
|
226 candidate = button; |
|
227 bestScore = score; |
|
228 } |
|
229 break; |
|
230 case Qt::Key_Down: |
|
231 if (p.y() > goal.y()) { |
|
232 candidate = button; |
|
233 bestScore = score; |
|
234 } |
|
235 break; |
|
236 case Qt::Key_Left: |
|
237 if (p.x() < goal.x()) { |
|
238 candidate = button; |
|
239 bestScore = score; |
|
240 } |
|
241 break; |
|
242 case Qt::Key_Right: |
|
243 if (p.x() > goal.x()) { |
|
244 candidate = button; |
|
245 bestScore = score; |
|
246 } |
|
247 break; |
|
248 } |
|
249 } |
|
250 } |
|
251 |
|
252 if (exclusive |
|
253 #ifdef QT_KEYPAD_NAVIGATION |
|
254 && !QApplication::keypadNavigationEnabled() |
|
255 #endif |
|
256 && candidate |
|
257 && focusButton->isChecked() |
|
258 && candidate->isCheckable()) |
|
259 candidate->click(); |
|
260 |
|
261 if (candidate) { |
|
262 if (key == Qt::Key_Up || key == Qt::Key_Left) |
|
263 candidate->setFocus(Qt::BacktabFocusReason); |
|
264 else |
|
265 candidate->setFocus(Qt::TabFocusReason); |
|
266 } |
|
267 } |
|
268 |
|
269 void HbAbstractButtonPrivate::fixFocusPolicy() |
|
270 { |
|
271 if (!autoExclusive) |
|
272 return; |
|
273 |
|
274 Q_Q( HbAbstractButton ); |
|
275 |
|
276 QList<HbAbstractButton *> buttonList = queryButtonList(); |
|
277 for (int i = 0; i < buttonList.count(); ++i) { |
|
278 HbAbstractButton *b = buttonList.at(i); |
|
279 if (!b->isCheckable()) |
|
280 continue; |
|
281 b->setFocusPolicy((Qt::FocusPolicy) ((b == q || !q->isCheckable()) |
|
282 ? (b->focusPolicy() | Qt::TabFocus) |
|
283 : (b->focusPolicy() & ~Qt::TabFocus))); |
|
284 } |
|
285 } |
|
286 |
|
287 void HbAbstractButtonPrivate::init() |
|
288 { |
|
289 Q_Q( HbAbstractButton ); |
|
290 |
|
291 q->setFocusPolicy(Qt::FocusPolicy(qApp->style()->styleHint(QStyle::SH_Button_FocusPolicy))); |
|
292 |
|
293 // FIXME: size policy is commented out b/c of a bug in Qt #236689, also in our bugtracker. |
|
294 //q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum, controlType)); |
|
295 |
|
296 //q->setForegroundRole(QPalette::ButtonText); TODO: check |
|
297 //q->setBackgroundRole(QPalette::Button); TODO: check |
|
298 } |
|
299 |
|
300 void HbAbstractButtonPrivate::refresh() |
|
301 { |
|
302 if (blockRefresh) |
|
303 return; |
|
304 |
|
305 Q_Q( HbAbstractButton ); |
|
306 |
|
307 q->updatePrimitives(); |
|
308 } |
|
309 |
|
310 void HbAbstractButtonPrivate::click() |
|
311 { |
|
312 Q_Q( HbAbstractButton ); |
|
313 |
|
314 down = false; |
|
315 blockRefresh = true; |
|
316 bool changeState = true; |
|
317 if (checked && queryCheckedButton() == q) { |
|
318 // the checked button of an exclusive or autoexclusive group cannot be unchecked |
|
319 if (autoExclusive) |
|
320 changeState = false; |
|
321 } |
|
322 |
|
323 QPointer<HbAbstractButton> guard(q); |
|
324 if (changeState) { |
|
325 q->nextCheckState(); |
|
326 if (!guard) |
|
327 return; |
|
328 } |
|
329 blockRefresh = false; |
|
330 refresh(); |
|
331 if (guard) |
|
332 emitReleased(); |
|
333 if (guard && !longPress) |
|
334 emitClicked(); |
|
335 if(guard && longPress) |
|
336 longPress = false; |
|
337 } |
|
338 |
|
339 void HbAbstractButtonPrivate::emitClicked() |
|
340 { |
|
341 Q_Q( HbAbstractButton ); |
|
342 |
|
343 QPointer<HbAbstractButton> guard(q); |
|
344 emit q->clicked(checked); |
|
345 } |
|
346 |
|
347 void HbAbstractButtonPrivate::emitPressed() |
|
348 { |
|
349 Q_Q( HbAbstractButton ); |
|
350 |
|
351 QPointer<HbAbstractButton> guard(q); |
|
352 emit q->pressed(); |
|
353 } |
|
354 |
|
355 void HbAbstractButtonPrivate::emitReleased() |
|
356 { |
|
357 Q_Q( HbAbstractButton ); |
|
358 |
|
359 QPointer<HbAbstractButton> guard(q); |
|
360 emit q->released(); |
|
361 } |
|
362 |
|
363 |
|
364 |
|
365 /*! |
|
366 @beta |
|
367 Constructs an abstract button with a \a parent. |
|
368 */ |
|
369 HbAbstractButton::HbAbstractButton(QGraphicsItem *parent) |
|
370 : HbWidget( *new HbAbstractButtonPrivate, parent) |
|
371 { |
|
372 Q_D( HbAbstractButton ); |
|
373 d->init(); |
|
374 } |
|
375 |
|
376 /*! |
|
377 @beta |
|
378 \internal |
|
379 */ |
|
380 HbAbstractButton::HbAbstractButton( HbAbstractButtonPrivate &dd, QGraphicsItem * parent ) : |
|
381 HbWidget( dd, parent ) |
|
382 { |
|
383 Q_D( HbAbstractButton ); |
|
384 d->init(); |
|
385 } |
|
386 |
|
387 /*! |
|
388 Destroys the button. |
|
389 */ |
|
390 HbAbstractButton::~HbAbstractButton() |
|
391 { |
|
392 } |
|
393 |
|
394 /* |
|
395 \property HbAbstractButton::shortcut |
|
396 \brief the mnemonic associated with the button |
|
397 |
|
398 void HbAbstractButton::setShortcut(const QKeySequence &key) |
|
399 { |
|
400 Q_ASSERT(mainWindow()); |
|
401 if (d->shortcutId != 0) |
|
402 mainWindow()->releaseShortcut(d->shortcutId); |
|
403 d->shortcut = key; |
|
404 d->shortcutId = mainWindow()->grabShortcut(key); |
|
405 } |
|
406 |
|
407 QKeySequence HbAbstractButton::shortcut() const |
|
408 { |
|
409 return d->shortcut; |
|
410 } |
|
411 */ |
|
412 |
|
413 /*! |
|
414 @beta |
|
415 Returns whether the button is checkable. |
|
416 |
|
417 By default, the button is not checkable. |
|
418 |
|
419 \sa setCheckable() checked |
|
420 */ |
|
421 bool HbAbstractButton::isCheckable() const |
|
422 { |
|
423 Q_D( const HbAbstractButton ); |
|
424 |
|
425 return d->checkable; |
|
426 } |
|
427 |
|
428 /*! |
|
429 @beta |
|
430 Sets whether the button is checkable. |
|
431 |
|
432 \sa isCheckable() |
|
433 */ |
|
434 void HbAbstractButton::setCheckable(bool checkable) |
|
435 { |
|
436 Q_D( HbAbstractButton ); |
|
437 |
|
438 if (d->checkable == checkable) |
|
439 return; |
|
440 |
|
441 d->checkable = checkable; |
|
442 d->checked = false; |
|
443 } |
|
444 |
|
445 /*! |
|
446 @beta |
|
447 Returns whether the button is checked. |
|
448 |
|
449 Only checkable buttons can be checked. By default, the button is unchecked. |
|
450 |
|
451 \sa setChecked() setCheckable() |
|
452 */ |
|
453 bool HbAbstractButton::isChecked() const |
|
454 { |
|
455 Q_D( const HbAbstractButton ); |
|
456 |
|
457 return d->checked; |
|
458 } |
|
459 |
|
460 /*! |
|
461 @beta |
|
462 Sets whether the button is checked. |
|
463 |
|
464 Only checkable buttons can be checked. By default, the button is unchecked. |
|
465 |
|
466 \sa isChecked() isCheckable() |
|
467 */ |
|
468 void HbAbstractButton::setChecked(bool checked) |
|
469 { |
|
470 Q_D( HbAbstractButton ); |
|
471 |
|
472 if (!d->checkable || d->checked == checked) { |
|
473 if (!d->blockRefresh) |
|
474 checkStateSet(); |
|
475 return; |
|
476 } |
|
477 if (!checked && d->queryCheckedButton() == this) { |
|
478 // the checked button of an exclusive or autoexclusive group cannot be unchecked |
|
479 if (d->autoExclusive) |
|
480 return; |
|
481 } |
|
482 |
|
483 QPointer<HbAbstractButton> guard(this); |
|
484 |
|
485 d->checked = checked; |
|
486 if (!d->blockRefresh) |
|
487 checkStateSet(); |
|
488 |
|
489 d->refresh(); |
|
490 |
|
491 if (guard && checked) |
|
492 d->notifyChecked(); |
|
493 if (guard) |
|
494 emit toggled(checked); |
|
495 } |
|
496 |
|
497 /*! |
|
498 @beta |
|
499 Returns whether the button is pressed down. |
|
500 |
|
501 If this property is \c true, the button is pressed down. |
|
502 The default value is \c false. |
|
503 |
|
504 \sa setDown() |
|
505 */ |
|
506 bool HbAbstractButton::isDown() const |
|
507 { |
|
508 Q_D( const HbAbstractButton ); |
|
509 |
|
510 return d->down; |
|
511 } |
|
512 |
|
513 /*! |
|
514 @beta |
|
515 Sets whether the button is pressed down. |
|
516 |
|
517 The signals pressed() and clicked() are not emitted |
|
518 if you set this property to \c true. |
|
519 |
|
520 \sa isDown() |
|
521 */ |
|
522 void HbAbstractButton::setDown(bool down) |
|
523 { |
|
524 Q_D( HbAbstractButton ); |
|
525 |
|
526 if (d->down == down) |
|
527 return; |
|
528 d->down = down; |
|
529 d->refresh(); |
|
530 if (d->autoRepeat && d->down) |
|
531 d->repeatTimer.start(d->autoRepeatDelay, this); |
|
532 else |
|
533 d->repeatTimer.stop(); |
|
534 } |
|
535 |
|
536 /*! |
|
537 @beta |
|
538 Returns whether autoRepeat is enabled. |
|
539 |
|
540 If autoRepeat is enabled, then the pressed(), released(), and clicked() signals are emitted at |
|
541 regular intervals when the button is down. autoRepeat is off by default. |
|
542 The initial delay and the repetition interval are defined in milliseconds by \l |
|
543 autoRepeatDelay and \l autoRepeatInterval. |
|
544 |
|
545 Note: If a button is pressed down by a shortcut key, then auto-repeat is enabled and timed by the |
|
546 system and not by this class. The pressed(), released(), and clicked() signals will be emitted |
|
547 like in the normal case. |
|
548 */ |
|
549 bool HbAbstractButton::autoRepeat() const |
|
550 { |
|
551 Q_D( const HbAbstractButton ); |
|
552 |
|
553 return d->autoRepeat; |
|
554 } |
|
555 |
|
556 /*! |
|
557 @beta |
|
558 Sets whether autoRepeat is enabled. |
|
559 */ |
|
560 void HbAbstractButton::setAutoRepeat(bool autoRepeat) |
|
561 { |
|
562 Q_D( HbAbstractButton ); |
|
563 |
|
564 if (d->autoRepeat == autoRepeat) |
|
565 return; |
|
566 d->autoRepeat = autoRepeat; |
|
567 if (d->autoRepeat && d->down) |
|
568 d->repeatTimer.start(d->autoRepeatDelay, this); |
|
569 else |
|
570 d->repeatTimer.stop(); |
|
571 } |
|
572 |
|
573 /*! |
|
574 @beta |
|
575 Returns the initial delay of auto-repetition in milliseconds. |
|
576 |
|
577 If \l autoRepeat is enabled, then autoRepeatDelay defines the initial |
|
578 delay in milliseconds before auto-repetition kicks in. |
|
579 |
|
580 \sa autoRepeat, autoRepeatInterval |
|
581 */ |
|
582 int HbAbstractButton::autoRepeatDelay() const |
|
583 { |
|
584 Q_D( const HbAbstractButton ); |
|
585 |
|
586 return d->autoRepeatDelay; |
|
587 } |
|
588 |
|
589 /*! |
|
590 @beta |
|
591 Sets the initial delay of auto-repetition in milliseconds. |
|
592 */ |
|
593 void HbAbstractButton::setAutoRepeatDelay(int autoRepeatDelay) |
|
594 { |
|
595 Q_D( HbAbstractButton ); |
|
596 |
|
597 d->autoRepeatDelay = autoRepeatDelay; |
|
598 } |
|
599 |
|
600 /*! |
|
601 @beta |
|
602 Returns the interval of auto-repetition. |
|
603 |
|
604 If \l autoRepeat is enabled, then autoRepeatInterval defines the |
|
605 length of the auto-repetition interval in millisecons. |
|
606 |
|
607 \sa autoRepeat, autoRepeatDelay |
|
608 */ |
|
609 int HbAbstractButton::autoRepeatInterval() const |
|
610 { |
|
611 Q_D( const HbAbstractButton ); |
|
612 |
|
613 return d->autoRepeatInterval; |
|
614 } |
|
615 |
|
616 /*! |
|
617 @beta |
|
618 Sets the interval of auto-repetition. |
|
619 */ |
|
620 void HbAbstractButton::setAutoRepeatInterval(int autoRepeatInterval) |
|
621 { |
|
622 Q_D( HbAbstractButton ); |
|
623 |
|
624 d->autoRepeatInterval = autoRepeatInterval; |
|
625 } |
|
626 |
|
627 /*! |
|
628 @beta |
|
629 Returns whether auto-exclusivity is enabled. |
|
630 |
|
631 If auto-exclusivity is enabled, checkable buttons that belong to the |
|
632 same parent widget behave as if they were part of the same |
|
633 exclusive button group. In an exclusive button group, only one button |
|
634 can be checked at any time; checking another button automatically |
|
635 unchecks the previously checked one. |
|
636 |
|
637 The property has no effect on buttons that belong to a button |
|
638 group. |
|
639 |
|
640 autoExclusive is off by default, except for radio buttons. |
|
641 |
|
642 \sa setAutoExclusive() |
|
643 */ |
|
644 bool HbAbstractButton::autoExclusive() const |
|
645 { |
|
646 Q_D( const HbAbstractButton ); |
|
647 |
|
648 return d->autoExclusive; |
|
649 } |
|
650 |
|
651 /*! |
|
652 @beta |
|
653 Sets whether auto-exclusivity is enabled. |
|
654 |
|
655 \sa autoExclusive() |
|
656 */ |
|
657 void HbAbstractButton::setAutoExclusive(bool autoExclusive) |
|
658 { |
|
659 Q_D( HbAbstractButton ); |
|
660 |
|
661 d->autoExclusive = autoExclusive; |
|
662 } |
|
663 |
|
664 |
|
665 |
|
666 /*! |
|
667 Performs an animated click: the button is pressed immediately, and |
|
668 released \a msec milliseconds later (the default is 100 ms). |
|
669 |
|
670 Calling this function again before the button was released will reset |
|
671 the release timer. |
|
672 |
|
673 All signals associated with a click are emitted as appropriate. |
|
674 |
|
675 This function does nothing if the button is \link setEnabled() |
|
676 disabled. \endlink |
|
677 |
|
678 \sa click() |
|
679 */ |
|
680 void HbAbstractButton::animateClick(int msec) |
|
681 { |
|
682 Q_D( HbAbstractButton ); |
|
683 |
|
684 if (!isEnabled()) |
|
685 return; |
|
686 if (d->checkable && focusPolicy() & Qt::ClickFocus) |
|
687 setFocus(); |
|
688 setDown(true); |
|
689 updatePrimitives(); |
|
690 |
|
691 if (!d->animateTimer.isActive()) |
|
692 d->emitPressed(); |
|
693 d->animateTimer.start(msec, this); |
|
694 } |
|
695 |
|
696 /*! |
|
697 Performs a click. |
|
698 |
|
699 All the usual signals associated with a click are emitted as |
|
700 appropriate. If the button is checkable, the state of the button is |
|
701 toggled. |
|
702 |
|
703 This function does nothing if the button is \link setEnabled() |
|
704 disabled. \endlink |
|
705 |
|
706 \sa animateClick() |
|
707 */ |
|
708 void HbAbstractButton::click() |
|
709 { |
|
710 Q_D( HbAbstractButton ); |
|
711 |
|
712 if (!isEnabled()) |
|
713 return; |
|
714 QPointer<HbAbstractButton> guard(this); |
|
715 d->down = true; |
|
716 d->emitPressed(); |
|
717 if (guard) { |
|
718 d->down = false; |
|
719 nextCheckState(); |
|
720 if (guard) |
|
721 d->emitReleased(); |
|
722 if (guard && !d->longPress) |
|
723 d->emitClicked(); |
|
724 } |
|
725 } |
|
726 |
|
727 /*! |
|
728 \fn void HbAbstractButton::toggle() |
|
729 |
|
730 Toggles the state of a checkable button. |
|
731 |
|
732 \sa checked |
|
733 */ |
|
734 void HbAbstractButton::toggle() |
|
735 { |
|
736 Q_D( HbAbstractButton ); |
|
737 |
|
738 setChecked(!d->checked); |
|
739 } |
|
740 |
|
741 /*! |
|
742 This virtual handler is called when setChecked() was called, |
|
743 unless it was called from within nextCheckState(). It allows |
|
744 subclasses to reset their intermediate button states. |
|
745 |
|
746 \sa nextCheckState() |
|
747 */ |
|
748 void HbAbstractButton::checkStateSet() |
|
749 { |
|
750 } |
|
751 |
|
752 /*! |
|
753 This virtual handler is called when a button is clicked. The |
|
754 default implementation calls setChecked(!isChecked()) if the button |
|
755 isCheckable(). It allows subclasses to implement intermediate button |
|
756 states. |
|
757 |
|
758 \sa checkStateSet() |
|
759 */ |
|
760 void HbAbstractButton::nextCheckState() |
|
761 { |
|
762 if (isCheckable()) |
|
763 setChecked(!isChecked()); |
|
764 } |
|
765 |
|
766 /*! |
|
767 Returns \c true if \a pos is inside the clickable button rectangle; |
|
768 otherwise returns \c false. |
|
769 |
|
770 By default, the clickable area is the entire widget. Subclasses |
|
771 may reimplement this function to provide support for clickable |
|
772 areas of different shapes and sizes. |
|
773 */ |
|
774 bool HbAbstractButton::hitButton(const QPointF &pos) const |
|
775 { |
|
776 return rect().contains(pos); |
|
777 } |
|
778 |
|
779 /*! |
|
780 Initializes \a option with the values from this HbAbstractButton. This method is useful for subclasses when they need a HbStyleOption, but don't want to fill in the pressed state information themselves. |
|
781 */ |
|
782 void HbAbstractButton::initStyleOption(HbStyleOption *option) const |
|
783 { |
|
784 HbWidget::initStyleOption(option); |
|
785 Q_ASSERT(option); |
|
786 option->state |= (isChecked() | isDown() ? QStyle::State_On : QStyle::State_Off); |
|
787 } |
|
788 |
|
789 /*! |
|
790 \reimp |
|
791 */ |
|
792 bool HbAbstractButton::event(QEvent *event) |
|
793 { |
|
794 // as opposed to other widgets, disabled buttons accept mouse |
|
795 // events. This avoids surprising click-through scenarios |
|
796 Q_D( HbAbstractButton ); |
|
797 if (!isEnabled()) { |
|
798 switch(event->type()) { |
|
799 case QEvent::TabletPress: |
|
800 case QEvent::TabletRelease: |
|
801 case QEvent::TabletMove: |
|
802 case QEvent::MouseButtonPress: |
|
803 case QEvent::MouseButtonRelease: |
|
804 case QEvent::MouseButtonDblClick: |
|
805 case QEvent::MouseMove: |
|
806 case QEvent::HoverMove: |
|
807 case QEvent::HoverEnter: |
|
808 case QEvent::HoverLeave: |
|
809 case QEvent::ContextMenu: |
|
810 #ifndef QT_NO_WHEELEVENT |
|
811 case QEvent::Wheel: |
|
812 #endif |
|
813 return true; |
|
814 default: |
|
815 break; |
|
816 } |
|
817 } |
|
818 if(event->type() == QEvent::MouseMove) { |
|
819 return true; |
|
820 } |
|
821 |
|
822 #ifndef QT_NO_SHORTCUT |
|
823 if (event->type() == QEvent::Shortcut) { |
|
824 QShortcutEvent *se = static_cast<QShortcutEvent *>(event); |
|
825 if (d->shortcutId != se->shortcutId()) |
|
826 return false; |
|
827 if (!se->isAmbiguous()) { |
|
828 if (!d->animateTimer.isActive()) |
|
829 animateClick(); |
|
830 } else { |
|
831 if (focusPolicy() != Qt::NoFocus) |
|
832 setFocus(Qt::ShortcutFocusReason); |
|
833 window()->setAttribute(Qt::WA_KeyboardFocusChange); |
|
834 } |
|
835 return true; |
|
836 } |
|
837 #endif |
|
838 return HbWidget::event(event); |
|
839 } |
|
840 |
|
841 /*! |
|
842 \reimp |
|
843 */ |
|
844 void HbAbstractButton::mousePressEvent(QGraphicsSceneMouseEvent *event) |
|
845 { |
|
846 Q_D( HbAbstractButton ); |
|
847 |
|
848 if (event->button() != Qt::LeftButton) { |
|
849 event->ignore(); |
|
850 return; |
|
851 } |
|
852 if (hitButton(event->pos())) { |
|
853 setDown(true); |
|
854 HbWidgetFeedback::triggered(this, Hb::InstantPressed); |
|
855 updatePrimitives(); |
|
856 d->emitPressed(); |
|
857 event->accept(); |
|
858 } else { |
|
859 event->ignore(); |
|
860 } |
|
861 } |
|
862 |
|
863 /*! |
|
864 \reimp |
|
865 */ |
|
866 void HbAbstractButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
|
867 { |
|
868 //This check is not required for s60 env |
|
869 #ifndef Q_OS_SYMBIAN |
|
870 if (event->button() != Qt::LeftButton) { |
|
871 event->ignore(); |
|
872 return; |
|
873 } |
|
874 #endif |
|
875 |
|
876 Q_D( HbAbstractButton ); |
|
877 |
|
878 //TODO: this code will be unnecessary when event filter of HbToolTipLabel will be implemented!! |
|
879 //HbToolTip::hideText(); |
|
880 |
|
881 if (!d->down) { |
|
882 event->ignore(); |
|
883 return; |
|
884 } |
|
885 if (hitButton(event->pos()) && !d->longPress) { |
|
886 HbWidgetFeedback::triggered(this, Hb::InstantClicked); |
|
887 } |
|
888 HbWidgetFeedback::triggered(this, Hb::InstantReleased); |
|
889 if (hitButton(event->pos())) { |
|
890 d->repeatTimer.stop(); |
|
891 d->click(); |
|
892 event->accept(); |
|
893 } else { |
|
894 setDown(false); |
|
895 event->ignore(); |
|
896 } |
|
897 d->longPress = false; |
|
898 } |
|
899 |
|
900 /*! |
|
901 \reimp |
|
902 */ |
|
903 void HbAbstractButton::mouseMoveEvent(QGraphicsSceneMouseEvent *event) |
|
904 { |
|
905 if (!(event->buttons() & Qt::LeftButton)) { |
|
906 event->ignore(); |
|
907 return; |
|
908 } |
|
909 |
|
910 Q_D( HbAbstractButton ); |
|
911 |
|
912 bool hit = hitButton(event->pos()); |
|
913 if (!hit) { |
|
914 //TODO: this code will be unnecessary when event filter of HbToolTipLabel will be implemented!! |
|
915 //HbToolTip::hideText(); |
|
916 } |
|
917 |
|
918 if (hit != d->down) { |
|
919 setDown(!d->down); |
|
920 updatePrimitives(); |
|
921 if (d->down) { |
|
922 // this call show tooltip for button, |
|
923 // for the case press, move outside and come back to same button. |
|
924 HbToolTip::showText(toolTip(), this); |
|
925 d->emitPressed(); |
|
926 HbWidgetFeedback::triggered(this, Hb::InstantPressed); |
|
927 } else { |
|
928 d->emitReleased(); |
|
929 d->longPress = false; |
|
930 HbWidgetFeedback::triggered(this, Hb::InstantReleased); |
|
931 } |
|
932 event->accept(); |
|
933 } else if (!hit) { |
|
934 event->ignore(); |
|
935 } |
|
936 } |
|
937 |
|
938 /*! |
|
939 \reimp |
|
940 */ |
|
941 void HbAbstractButton::keyPressEvent(QKeyEvent *event) |
|
942 { |
|
943 Q_D( HbAbstractButton ); |
|
944 |
|
945 bool next = true; |
|
946 switch (event->key()) { |
|
947 |
|
948 case Qt::Key_Select: |
|
949 case Qt::Key_Enter: |
|
950 case Qt::Key_Return: |
|
951 if (!event->isAutoRepeat()) { |
|
952 setDown(true); |
|
953 updatePrimitives(); |
|
954 d->emitPressed(); |
|
955 } |
|
956 break; |
|
957 case Qt::Key_Up: |
|
958 case Qt::Key_Left: |
|
959 next = false; |
|
960 // fall through |
|
961 case Qt::Key_Right: |
|
962 case Qt::Key_Down: |
|
963 #ifdef QT_KEYPAD_NAVIGATION |
|
964 if (QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right)) { |
|
965 event->ignore(); |
|
966 return; |
|
967 } |
|
968 #endif |
|
969 if (d->autoExclusive) { |
|
970 // ### Using qobject_cast to check if the parent is a viewport of |
|
971 // QAbstractItemView is a crude hack, and should be revisited and |
|
972 // cleaned up when fixing task 194373. It's here to ensure that we |
|
973 // keep compatibility outside QAbstractItemView. |
|
974 d->moveFocus(event->key()); |
|
975 if (hasFocus()) // nothing happend, propagate |
|
976 event->ignore(); |
|
977 } else { |
|
978 focusNextPrevChild(next); |
|
979 } |
|
980 break; |
|
981 case Qt::Key_Escape: |
|
982 if (d->down) { |
|
983 setDown(false); |
|
984 updatePrimitives(); |
|
985 d->emitReleased(); |
|
986 break; |
|
987 } |
|
988 // fall through |
|
989 default: |
|
990 event->ignore(); |
|
991 } |
|
992 } |
|
993 |
|
994 /*! |
|
995 \reimp |
|
996 */ |
|
997 void HbAbstractButton::keyReleaseEvent(QKeyEvent *event) |
|
998 { |
|
999 Q_D( HbAbstractButton ); |
|
1000 |
|
1001 if (!event->isAutoRepeat()) |
|
1002 d->repeatTimer.stop(); |
|
1003 |
|
1004 switch (event->key()) { |
|
1005 case Qt::Key_Select: |
|
1006 case Qt::Key_Enter: |
|
1007 case Qt::Key_Return: { |
|
1008 if (!event->isAutoRepeat() /*&& d->down*/) { |
|
1009 if (d->down && !d->longPress) { |
|
1010 HbWidgetFeedback::triggered(this, Hb::InstantClicked); |
|
1011 } |
|
1012 d->click(); |
|
1013 } |
|
1014 break; |
|
1015 } |
|
1016 default: |
|
1017 event->ignore(); |
|
1018 } |
|
1019 } |
|
1020 |
|
1021 /*! |
|
1022 \reimp |
|
1023 */ |
|
1024 void HbAbstractButton::timerEvent(QTimerEvent *event) |
|
1025 { |
|
1026 Q_D( HbAbstractButton ); |
|
1027 |
|
1028 if (event->timerId() == d->repeatTimer.timerId()) { |
|
1029 d->repeatTimer.start(d->autoRepeatInterval, this); |
|
1030 if (d->down) { |
|
1031 QPointer<HbAbstractButton> guard(this); |
|
1032 d->emitReleased(); |
|
1033 if (guard) { |
|
1034 d->emitClicked(); |
|
1035 } |
|
1036 if (guard) { |
|
1037 d->emitPressed(); |
|
1038 HbWidgetFeedback::triggered(this, Hb::InstantKeyRepeated); |
|
1039 } |
|
1040 } |
|
1041 } else if (event->timerId() == d->animateTimer.timerId()) { |
|
1042 d->animateTimer.stop(); |
|
1043 d->click(); |
|
1044 } |
|
1045 } |
|
1046 |
|
1047 |
|
1048 /*! |
|
1049 \reimp |
|
1050 */ |
|
1051 void HbAbstractButton::focusInEvent(QFocusEvent *event) |
|
1052 { |
|
1053 Q_D( HbAbstractButton ); |
|
1054 |
|
1055 #ifdef QT_KEYPAD_NAVIGATION |
|
1056 if (!QApplication::keypadNavigationEnabled()) |
|
1057 #endif |
|
1058 d->fixFocusPolicy(); |
|
1059 HbWidget::focusInEvent(event); |
|
1060 } |
|
1061 |
|
1062 /*! |
|
1063 \reimp |
|
1064 */ |
|
1065 void HbAbstractButton::changeEvent(QEvent *event) |
|
1066 { |
|
1067 Q_D( HbAbstractButton ); |
|
1068 |
|
1069 switch (event->type()) { |
|
1070 case QEvent::EnabledChange: |
|
1071 if (!isEnabled()) |
|
1072 setDown(false); |
|
1073 break; |
|
1074 default: |
|
1075 d->sizeHint = QSize(); |
|
1076 break; |
|
1077 } |
|
1078 HbWidget::changeEvent(event); |
|
1079 } |
|
1080 |
|
1081 /*! |
|
1082 \reimp |
|
1083 */ |
|
1084 void HbAbstractButton::polish(HbStyleParameters& params) |
|
1085 { |
|
1086 Q_D( HbAbstractButton ); |
|
1087 |
|
1088 if (d->mSizeHintPolish) { |
|
1089 d->mSizeHintPolish = false; |
|
1090 return; |
|
1091 } |
|
1092 d->mRepolishRequested = false; |
|
1093 HbWidget::polish(params); |
|
1094 } |
|
1095 |
|
1096 /*! |
|
1097 \reimp |
|
1098 */ |
|
1099 QSizeF HbAbstractButton::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const |
|
1100 { |
|
1101 Q_D(const HbAbstractButton); |
|
1102 if (d->mRepolishRequested && isVisible()) { |
|
1103 d->mRepolishRequested = false; |
|
1104 // force the polish event in order to get the real size |
|
1105 QEvent polishEvent(QEvent::Polish); |
|
1106 QCoreApplication::sendEvent(const_cast<HbAbstractButton *>(this), &polishEvent); |
|
1107 d->mSizeHintPolish = true; |
|
1108 } |
|
1109 return HbWidget::sizeHint(which, constraint); |
|
1110 } |
|
1111 |
|
1112 /*! |
|
1113 \fn void HbAbstractButton::pressed() |
|
1114 |
|
1115 This signal is emitted when the button is pressed down. |
|
1116 |
|
1117 \sa released(), clicked() |
|
1118 */ |
|
1119 |
|
1120 /*! |
|
1121 \fn void HbAbstractButton::released() |
|
1122 |
|
1123 This signal is emitted when the button is released. |
|
1124 |
|
1125 \sa pressed(), clicked(), toggled() |
|
1126 */ |
|
1127 |
|
1128 /*! |
|
1129 \fn void HbAbstractButton::clicked(bool checked) |
|
1130 |
|
1131 This signal is emitted when the button is activated (i.e. pressed down |
|
1132 then released while the stylus is inside the button), when the |
|
1133 shortcut key is typed, or when click() or animateClick() is called. |
|
1134 Notably, this signal is \e not emitted if you call setDown(), |
|
1135 setChecked() or toggle(). |
|
1136 |
|
1137 If the button is checkable, \a checked is true if the button is |
|
1138 checked, or false if the button is unchecked. |
|
1139 |
|
1140 \sa pressed(), released(), toggled() |
|
1141 */ |
|
1142 |
|
1143 /*! |
|
1144 \fn void HbAbstractButton::toggled(bool checked) |
|
1145 |
|
1146 This signal is emitted whenever a checkable button changes its state. |
|
1147 \a checked is true if the button is checked, or false if the button is |
|
1148 unchecked. |
|
1149 |
|
1150 This may be the result of a user action, click() slot activation, |
|
1151 or because setChecked() was called. |
|
1152 |
|
1153 \sa checked, clicked() |
|
1154 */ |
|
1155 |
|
1156 #include "moc_hbabstractbutton.cpp" |