1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qquickabstractbutton_p.h" |
38 | #include "qquickabstractbutton_p_p.h" |
39 | #include "qquickbuttongroup_p.h" |
40 | #include "qquickaction_p.h" |
41 | #include "qquickaction_p_p.h" |
42 | #include "qquickshortcutcontext_p_p.h" |
43 | #include "qquickdeferredexecute_p_p.h" |
44 | |
45 | #include <QtGui/qstylehints.h> |
46 | #include <QtGui/qguiapplication.h> |
47 | #if QT_CONFIG(shortcut) |
48 | # include <QtGui/private/qshortcutmap_p.h> |
49 | #endif |
50 | #include <QtGui/private/qguiapplication_p.h> |
51 | #include <QtQuick/private/qquickevents_p_p.h> |
52 | #include <QtQml/qqmllist.h> |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | /*! |
57 | \qmltype AbstractButton |
58 | \inherits Control |
59 | //! \instantiates QQuickAbstractButton |
60 | \inqmlmodule QtQuick.Controls |
61 | \since 5.7 |
62 | \ingroup qtquickcontrols2-buttons |
63 | \brief Abstract base type providing functionality common to buttons. |
64 | |
65 | AbstractButton provides the interface for controls with button-like |
66 | behavior; for example, push buttons and checkable controls like |
67 | radio buttons and check boxes. As an abstract control, it has no delegate |
68 | implementations, leaving them to the types that derive from it. |
69 | |
70 | \sa ButtonGroup, {Button Controls} |
71 | */ |
72 | |
73 | /*! |
74 | \qmlsignal QtQuick.Controls::AbstractButton::pressed() |
75 | |
76 | This signal is emitted when the button is interactively pressed by the user via touch, mouse, or keyboard. |
77 | */ |
78 | |
79 | /*! |
80 | \qmlsignal QtQuick.Controls::AbstractButton::released() |
81 | |
82 | This signal is emitted when the button is interactively released by the user via touch, mouse, or keyboard. |
83 | */ |
84 | |
85 | /*! |
86 | \qmlsignal QtQuick.Controls::AbstractButton::canceled() |
87 | |
88 | This signal is emitted when the button loses mouse grab |
89 | while being pressed, or when it would emit the \l released |
90 | signal but the mouse cursor is not inside the button. |
91 | */ |
92 | |
93 | /*! |
94 | \qmlsignal QtQuick.Controls::AbstractButton::clicked() |
95 | |
96 | This signal is emitted when the button is interactively clicked by the user via touch, mouse, or keyboard. |
97 | */ |
98 | |
99 | /*! |
100 | \since QtQuick.Controls 2.2 (Qt 5.9) |
101 | \qmlsignal QtQuick.Controls::AbstractButton::toggled() |
102 | |
103 | This signal is emitted when a checkable button is interactively toggled by the user via touch, mouse, or keyboard. |
104 | */ |
105 | |
106 | /*! |
107 | \qmlsignal QtQuick.Controls::AbstractButton::pressAndHold() |
108 | |
109 | This signal is emitted when the button is interactively pressed and held down by the user via touch or mouse. |
110 | It is not emitted when \l autoRepeat is enabled. |
111 | */ |
112 | |
113 | /*! |
114 | \qmlsignal QtQuick.Controls::AbstractButton::doubleClicked() |
115 | |
116 | This signal is emitted when the button is interactively double clicked by the user via touch or mouse. |
117 | */ |
118 | |
119 | void QQuickAbstractButtonPrivate::setPressPoint(const QPointF &point) |
120 | { |
121 | pressPoint = point; |
122 | setMovePoint(point); |
123 | } |
124 | |
125 | void QQuickAbstractButtonPrivate::setMovePoint(const QPointF &point) |
126 | { |
127 | Q_Q(QQuickAbstractButton); |
128 | bool xChange = !qFuzzyCompare(p1: point.x(), p2: movePoint.x()); |
129 | bool yChange = !qFuzzyCompare(p1: point.y(), p2: movePoint.y()); |
130 | movePoint = point; |
131 | if (xChange) |
132 | emit q->pressXChanged(); |
133 | if (yChange) |
134 | emit q->pressYChanged(); |
135 | } |
136 | |
137 | void QQuickAbstractButtonPrivate::handlePress(const QPointF &point) |
138 | { |
139 | Q_Q(QQuickAbstractButton); |
140 | QQuickControlPrivate::handlePress(point); |
141 | setPressPoint(point); |
142 | q->setPressed(true); |
143 | |
144 | emit q->pressed(); |
145 | |
146 | if (autoRepeat) |
147 | startRepeatDelay(); |
148 | else if (touchId != -1 || Qt::LeftButton == (pressButtons & Qt::LeftButton)) |
149 | startPressAndHold(); |
150 | else |
151 | stopPressAndHold(); |
152 | } |
153 | |
154 | void QQuickAbstractButtonPrivate::handleMove(const QPointF &point) |
155 | { |
156 | Q_Q(QQuickAbstractButton); |
157 | QQuickControlPrivate::handleMove(point); |
158 | setMovePoint(point); |
159 | q->setPressed(keepPressed || q->contains(point)); |
160 | |
161 | if (!pressed && autoRepeat) |
162 | stopPressRepeat(); |
163 | else if (holdTimer > 0 && (!pressed || QLineF(pressPoint, point).length() > QGuiApplication::styleHints()->startDragDistance())) |
164 | stopPressAndHold(); |
165 | } |
166 | |
167 | void QQuickAbstractButtonPrivate::handleRelease(const QPointF &point) |
168 | { |
169 | Q_Q(QQuickAbstractButton); |
170 | QQuickControlPrivate::handleRelease(point); |
171 | bool wasPressed = pressed; |
172 | setPressPoint(point); |
173 | q->setPressed(false); |
174 | pressButtons = Qt::NoButton; |
175 | |
176 | if (!wasHeld && (keepPressed || q->contains(point))) |
177 | q->nextCheckState(); |
178 | |
179 | if (wasPressed) { |
180 | emit q->released(); |
181 | if (!wasHeld && !wasDoubleClick) |
182 | trigger(); |
183 | } else { |
184 | emit q->canceled(); |
185 | } |
186 | |
187 | if (autoRepeat) |
188 | stopPressRepeat(); |
189 | else |
190 | stopPressAndHold(); |
191 | |
192 | wasDoubleClick = false; |
193 | } |
194 | |
195 | void QQuickAbstractButtonPrivate::handleUngrab() |
196 | { |
197 | Q_Q(QQuickAbstractButton); |
198 | QQuickControlPrivate::handleUngrab(); |
199 | pressButtons = Qt::NoButton; |
200 | if (!pressed) |
201 | return; |
202 | |
203 | q->setPressed(false); |
204 | stopPressRepeat(); |
205 | stopPressAndHold(); |
206 | wasDoubleClick = false; |
207 | emit q->canceled(); |
208 | } |
209 | |
210 | bool QQuickAbstractButtonPrivate::acceptKeyClick(Qt::Key key) const |
211 | { |
212 | return key == Qt::Key_Space; |
213 | } |
214 | |
215 | bool QQuickAbstractButtonPrivate::isPressAndHoldConnected() |
216 | { |
217 | Q_Q(QQuickAbstractButton); |
218 | static const QMetaMethod method = [&]() { |
219 | const auto signal = &QQuickAbstractButton::pressAndHold; |
220 | return QMetaMethod::fromSignal(signal); |
221 | }(); |
222 | return q->isSignalConnected(signal: method); |
223 | } |
224 | |
225 | bool QQuickAbstractButtonPrivate::isDoubleClickConnected() |
226 | { |
227 | Q_Q(QQuickAbstractButton); |
228 | static const QMetaMethod method = [&]() { |
229 | const auto signal = &QQuickAbstractButton::doubleClicked; |
230 | return QMetaMethod::fromSignal(signal); |
231 | }(); |
232 | return q->isSignalConnected(signal: method); |
233 | } |
234 | |
235 | void QQuickAbstractButtonPrivate::startPressAndHold() |
236 | { |
237 | Q_Q(QQuickAbstractButton); |
238 | wasHeld = false; |
239 | stopPressAndHold(); |
240 | if (isPressAndHoldConnected()) |
241 | holdTimer = q->startTimer(interval: QGuiApplication::styleHints()->mousePressAndHoldInterval()); |
242 | } |
243 | |
244 | void QQuickAbstractButtonPrivate::stopPressAndHold() |
245 | { |
246 | Q_Q(QQuickAbstractButton); |
247 | if (holdTimer > 0) { |
248 | q->killTimer(id: holdTimer); |
249 | holdTimer = 0; |
250 | } |
251 | } |
252 | |
253 | void QQuickAbstractButtonPrivate::startRepeatDelay() |
254 | { |
255 | Q_Q(QQuickAbstractButton); |
256 | stopPressRepeat(); |
257 | delayTimer = q->startTimer(interval: repeatDelay); |
258 | } |
259 | |
260 | void QQuickAbstractButtonPrivate::startPressRepeat() |
261 | { |
262 | Q_Q(QQuickAbstractButton); |
263 | stopPressRepeat(); |
264 | repeatTimer = q->startTimer(interval: repeatInterval); |
265 | } |
266 | |
267 | void QQuickAbstractButtonPrivate::stopPressRepeat() |
268 | { |
269 | Q_Q(QQuickAbstractButton); |
270 | if (delayTimer > 0) { |
271 | q->killTimer(id: delayTimer); |
272 | delayTimer = 0; |
273 | } |
274 | if (repeatTimer > 0) { |
275 | q->killTimer(id: repeatTimer); |
276 | repeatTimer = 0; |
277 | } |
278 | } |
279 | |
280 | #if QT_CONFIG(shortcut) |
281 | void QQuickAbstractButtonPrivate::grabShortcut() |
282 | { |
283 | Q_Q(QQuickAbstractButton); |
284 | if (shortcut.isEmpty()) |
285 | return; |
286 | |
287 | shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(owner: q, key: shortcut, context: Qt::WindowShortcut, matcher: QQuickShortcutContext::matcher); |
288 | |
289 | if (!q->isEnabled()) |
290 | QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable: false, id: shortcutId, owner: q); |
291 | } |
292 | |
293 | void QQuickAbstractButtonPrivate::ungrabShortcut() |
294 | { |
295 | Q_Q(QQuickAbstractButton); |
296 | if (!shortcutId) |
297 | return; |
298 | |
299 | QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id: shortcutId, owner: q); |
300 | shortcutId = 0; |
301 | } |
302 | #endif |
303 | |
304 | void QQuickAbstractButtonPrivate::actionTextChange() |
305 | { |
306 | Q_Q(QQuickAbstractButton); |
307 | if (explicitText) |
308 | return; |
309 | |
310 | q->buttonChange(change: QQuickAbstractButton::ButtonTextChange); |
311 | } |
312 | |
313 | void QQuickAbstractButtonPrivate::setText(const QString &newText, bool isExplicit) |
314 | { |
315 | Q_Q(QQuickAbstractButton); |
316 | const QString oldText = q->text(); |
317 | explicitText = isExplicit; |
318 | text = newText; |
319 | if (oldText == q->text()) |
320 | return; |
321 | |
322 | q->buttonChange(change: QQuickAbstractButton::ButtonTextChange); |
323 | } |
324 | |
325 | void QQuickAbstractButtonPrivate::updateEffectiveIcon() |
326 | { |
327 | Q_Q(QQuickAbstractButton); |
328 | // We store effectiveIcon because we need to be able to tell if the icon has actually changed. |
329 | // If we only stored our icon and the action's icon, and resolved in the getter, we'd have |
330 | // no way of knowing what the old value was here. As an added benefit, we only resolve when |
331 | // something has changed, as opposed to doing it unconditionally in the icon() getter. |
332 | const QQuickIcon newEffectiveIcon = action ? icon.resolve(other: action->icon()) : icon; |
333 | if (newEffectiveIcon == effectiveIcon) |
334 | return; |
335 | |
336 | effectiveIcon = newEffectiveIcon; |
337 | emit q->iconChanged(); |
338 | } |
339 | |
340 | void QQuickAbstractButtonPrivate::click() |
341 | { |
342 | Q_Q(QQuickAbstractButton); |
343 | if (effectiveEnable) |
344 | emit q->clicked(); |
345 | } |
346 | |
347 | void QQuickAbstractButtonPrivate::trigger() |
348 | { |
349 | Q_Q(QQuickAbstractButton); |
350 | const bool wasEnabled = effectiveEnable; |
351 | if (action && action->isEnabled()) |
352 | QQuickActionPrivate::get(action)->trigger(q, doToggle: false); |
353 | if (wasEnabled && (!action || !action->isEnabled())) |
354 | emit q->clicked(); |
355 | } |
356 | |
357 | void QQuickAbstractButtonPrivate::toggle(bool value) |
358 | { |
359 | Q_Q(QQuickAbstractButton); |
360 | const bool wasChecked = checked; |
361 | q->setChecked(value); |
362 | if (wasChecked != checked) |
363 | emit q->toggled(); |
364 | } |
365 | |
366 | static inline QString indicatorName() { return QStringLiteral("indicator" ); } |
367 | |
368 | void QQuickAbstractButtonPrivate::cancelIndicator() |
369 | { |
370 | Q_Q(QQuickAbstractButton); |
371 | quickCancelDeferred(object: q, property: indicatorName()); |
372 | } |
373 | |
374 | void QQuickAbstractButtonPrivate::executeIndicator(bool complete) |
375 | { |
376 | Q_Q(QQuickAbstractButton); |
377 | if (indicator.wasExecuted()) |
378 | return; |
379 | |
380 | if (!indicator || complete) |
381 | quickBeginDeferred(object: q, property: indicatorName(), delegate&: indicator); |
382 | if (complete) |
383 | quickCompleteDeferred(object: q, property: indicatorName(), delegate&: indicator); |
384 | } |
385 | |
386 | void QQuickAbstractButtonPrivate::itemImplicitWidthChanged(QQuickItem *item) |
387 | { |
388 | Q_Q(QQuickAbstractButton); |
389 | QQuickControlPrivate::itemImplicitWidthChanged(item); |
390 | if (item == indicator) |
391 | emit q->implicitIndicatorWidthChanged(); |
392 | } |
393 | |
394 | void QQuickAbstractButtonPrivate::itemImplicitHeightChanged(QQuickItem *item) |
395 | { |
396 | Q_Q(QQuickAbstractButton); |
397 | QQuickControlPrivate::itemImplicitHeightChanged(item); |
398 | if (item == indicator) |
399 | emit q->implicitIndicatorHeightChanged(); |
400 | } |
401 | |
402 | void QQuickAbstractButtonPrivate::itemDestroyed(QQuickItem *item) |
403 | { |
404 | Q_Q(QQuickAbstractButton); |
405 | QQuickControlPrivate::itemDestroyed(item); |
406 | if (item == indicator) { |
407 | indicator = nullptr; |
408 | emit q->implicitIndicatorWidthChanged(); |
409 | emit q->implicitIndicatorHeightChanged(); |
410 | } |
411 | } |
412 | |
413 | QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const |
414 | { |
415 | Q_Q(const QQuickAbstractButton); |
416 | if (group) |
417 | return qobject_cast<QQuickAbstractButton *>(object: group->checkedButton()); |
418 | |
419 | const QList<QQuickAbstractButton *> buttons = findExclusiveButtons(); |
420 | // TODO: A singular QRadioButton can be unchecked, which seems logical, |
421 | // because there's nothing to be exclusive with. However, a RadioButton |
422 | // from QtQuick.Controls 1.x can never be unchecked, which is the behavior |
423 | // that QQuickRadioButton adopted. Uncommenting the following count check |
424 | // gives the QRadioButton behavior. Notice that tst_radiobutton.qml needs |
425 | // to be updated. |
426 | if (!autoExclusive /*|| buttons.count() == 1*/) |
427 | return nullptr; |
428 | |
429 | for (QQuickAbstractButton *button : buttons) { |
430 | if (button->isChecked() && button != q) |
431 | return button; |
432 | } |
433 | return checked ? const_cast<QQuickAbstractButton *>(q) : nullptr; |
434 | } |
435 | |
436 | QList<QQuickAbstractButton *> QQuickAbstractButtonPrivate::findExclusiveButtons() const |
437 | { |
438 | QList<QQuickAbstractButton *> buttons; |
439 | if (group) { |
440 | QQmlListProperty<QQuickAbstractButton> groupButtons = group->buttons(); |
441 | int count = groupButtons.count(&groupButtons); |
442 | for (int i = 0; i < count; ++i) { |
443 | QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: groupButtons.at(&groupButtons, i)); |
444 | if (button) |
445 | buttons += button; |
446 | } |
447 | } else if (parentItem) { |
448 | const auto childItems = parentItem->childItems(); |
449 | for (QQuickItem *child : childItems) { |
450 | QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: child); |
451 | if (button && button->autoExclusive() && !QQuickAbstractButtonPrivate::get(button)->group) |
452 | buttons += button; |
453 | } |
454 | } |
455 | return buttons; |
456 | } |
457 | |
458 | QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent) |
459 | : QQuickControl(*(new QQuickAbstractButtonPrivate), parent) |
460 | { |
461 | setActiveFocusOnTab(true); |
462 | setFocusPolicy(Qt::StrongFocus); |
463 | setAcceptedMouseButtons(Qt::LeftButton); |
464 | setAcceptTouchEvents(true); |
465 | #if QT_CONFIG(cursor) |
466 | setCursor(Qt::ArrowCursor); |
467 | #endif |
468 | } |
469 | |
470 | QQuickAbstractButton::QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent) |
471 | : QQuickControl(dd, parent) |
472 | { |
473 | setActiveFocusOnTab(true); |
474 | setFocusPolicy(Qt::StrongFocus); |
475 | setAcceptedMouseButtons(Qt::LeftButton); |
476 | setAcceptTouchEvents(true); |
477 | #if QT_CONFIG(cursor) |
478 | setCursor(Qt::ArrowCursor); |
479 | #endif |
480 | } |
481 | |
482 | QQuickAbstractButton::~QQuickAbstractButton() |
483 | { |
484 | Q_D(QQuickAbstractButton); |
485 | d->removeImplicitSizeListener(item: d->indicator); |
486 | if (d->group) |
487 | d->group->removeButton(button: this); |
488 | #if QT_CONFIG(shortcut) |
489 | d->ungrabShortcut(); |
490 | #endif |
491 | } |
492 | |
493 | /*! |
494 | \qmlproperty string QtQuick.Controls::AbstractButton::text |
495 | |
496 | This property holds a textual description of the button. |
497 | |
498 | \note The text is used for accessibility purposes, so it makes sense to |
499 | set a textual description even if the content item is an image. |
500 | |
501 | \sa icon, display, {Control::contentItem}{contentItem} |
502 | */ |
503 | QString QQuickAbstractButton::text() const |
504 | { |
505 | Q_D(const QQuickAbstractButton); |
506 | return d->explicitText || !d->action ? d->text : d->action->text(); |
507 | } |
508 | |
509 | void QQuickAbstractButton::setText(const QString &text) |
510 | { |
511 | Q_D(QQuickAbstractButton); |
512 | d->setText(newText: text, isExplicit: true); |
513 | } |
514 | |
515 | void QQuickAbstractButton::resetText() |
516 | { |
517 | Q_D(QQuickAbstractButton); |
518 | d->setText(newText: QString(), isExplicit: false); |
519 | } |
520 | |
521 | /*! |
522 | \qmlproperty bool QtQuick.Controls::AbstractButton::down |
523 | |
524 | This property holds whether the button is visually down. |
525 | |
526 | Unless explicitly set, this property follows the value of \l pressed. To |
527 | return to the default value, set this property to \c undefined. |
528 | |
529 | \sa pressed |
530 | */ |
531 | bool QQuickAbstractButton::isDown() const |
532 | { |
533 | Q_D(const QQuickAbstractButton); |
534 | return d->down; |
535 | } |
536 | |
537 | void QQuickAbstractButton::setDown(bool down) |
538 | { |
539 | Q_D(QQuickAbstractButton); |
540 | d->explicitDown = true; |
541 | |
542 | if (d->down == down) |
543 | return; |
544 | |
545 | d->down = down; |
546 | emit downChanged(); |
547 | } |
548 | |
549 | void QQuickAbstractButton::resetDown() |
550 | { |
551 | Q_D(QQuickAbstractButton); |
552 | if (!d->explicitDown) |
553 | return; |
554 | |
555 | setDown(d->pressed); |
556 | d->explicitDown = false; |
557 | } |
558 | |
559 | /*! |
560 | \qmlproperty bool QtQuick.Controls::AbstractButton::pressed |
561 | \readonly |
562 | |
563 | This property holds whether the button is physically pressed. A button can |
564 | be pressed by either touch or key events. |
565 | |
566 | \sa down |
567 | */ |
568 | bool QQuickAbstractButton::isPressed() const |
569 | { |
570 | Q_D(const QQuickAbstractButton); |
571 | return d->pressed; |
572 | } |
573 | |
574 | void QQuickAbstractButton::setPressed(bool isPressed) |
575 | { |
576 | Q_D(QQuickAbstractButton); |
577 | if (d->pressed == isPressed) |
578 | return; |
579 | |
580 | d->pressed = isPressed; |
581 | setAccessibleProperty(propertyName: "pressed" , value: isPressed); |
582 | emit pressedChanged(); |
583 | buttonChange(change: ButtonPressedChanged); |
584 | |
585 | if (!d->explicitDown) { |
586 | setDown(d->pressed); |
587 | d->explicitDown = false; |
588 | } |
589 | } |
590 | |
591 | /*! |
592 | \qmlproperty bool QtQuick.Controls::AbstractButton::checked |
593 | |
594 | This property holds whether the button is checked. |
595 | |
596 | \sa checkable |
597 | */ |
598 | bool QQuickAbstractButton::isChecked() const |
599 | { |
600 | Q_D(const QQuickAbstractButton); |
601 | return d->checked; |
602 | } |
603 | |
604 | void QQuickAbstractButton::setChecked(bool checked) |
605 | { |
606 | Q_D(QQuickAbstractButton); |
607 | if (d->checked == checked) |
608 | return; |
609 | |
610 | if (checked && !d->checkable) |
611 | setCheckable(true); |
612 | |
613 | d->checked = checked; |
614 | if (d->action) |
615 | d->action->setChecked(checked); |
616 | setAccessibleProperty(propertyName: "checked" , value: checked); |
617 | buttonChange(change: ButtonCheckedChange); |
618 | emit checkedChanged(); |
619 | } |
620 | |
621 | /*! |
622 | \qmlproperty bool QtQuick.Controls::AbstractButton::checkable |
623 | |
624 | This property holds whether the button is checkable. |
625 | |
626 | A checkable button toggles between checked (on) and unchecked (off) when |
627 | the user clicks on it or presses the space bar while the button has active |
628 | focus. |
629 | |
630 | Setting \l checked to \c true forces this property to \c true. |
631 | |
632 | The default value is \c false. |
633 | |
634 | \sa checked |
635 | */ |
636 | bool QQuickAbstractButton::isCheckable() const |
637 | { |
638 | Q_D(const QQuickAbstractButton); |
639 | return d->checkable; |
640 | } |
641 | |
642 | void QQuickAbstractButton::setCheckable(bool checkable) |
643 | { |
644 | Q_D(QQuickAbstractButton); |
645 | if (d->checkable == checkable) |
646 | return; |
647 | |
648 | d->checkable = checkable; |
649 | if (d->action) |
650 | d->action->setCheckable(checkable); |
651 | setAccessibleProperty(propertyName: "checkable" , value: checkable); |
652 | buttonChange(change: ButtonCheckableChange); |
653 | emit checkableChanged(); |
654 | } |
655 | |
656 | /*! |
657 | \qmlproperty bool QtQuick.Controls::AbstractButton::autoExclusive |
658 | |
659 | This property holds whether auto-exclusivity is enabled. |
660 | |
661 | If auto-exclusivity is enabled, checkable buttons that belong to the same |
662 | parent item behave as if they were part of the same ButtonGroup. Only |
663 | one button can be checked at any time; checking another button automatically |
664 | unchecks the previously checked one. |
665 | |
666 | \note The property has no effect on buttons that belong to a ButtonGroup. |
667 | |
668 | RadioButton and TabButton are auto-exclusive by default. |
669 | */ |
670 | bool QQuickAbstractButton::autoExclusive() const |
671 | { |
672 | Q_D(const QQuickAbstractButton); |
673 | return d->autoExclusive; |
674 | } |
675 | |
676 | void QQuickAbstractButton::setAutoExclusive(bool exclusive) |
677 | { |
678 | Q_D(QQuickAbstractButton); |
679 | if (d->autoExclusive == exclusive) |
680 | return; |
681 | |
682 | d->autoExclusive = exclusive; |
683 | emit autoExclusiveChanged(); |
684 | } |
685 | |
686 | /*! |
687 | \qmlproperty bool QtQuick.Controls::AbstractButton::autoRepeat |
688 | |
689 | This property holds whether the button repeats \l pressed(), \l released() |
690 | and \l clicked() signals while the button is pressed and held down. |
691 | |
692 | If this property is set to \c true, the \l pressAndHold() signal will not |
693 | be emitted. |
694 | |
695 | The default value is \c false. |
696 | |
697 | The initial delay and the repetition interval are defined in milliseconds |
698 | by \l autoRepeatDelay and \l autoRepeatInterval. |
699 | */ |
700 | bool QQuickAbstractButton::autoRepeat() const |
701 | { |
702 | Q_D(const QQuickAbstractButton); |
703 | return d->autoRepeat; |
704 | } |
705 | |
706 | void QQuickAbstractButton::setAutoRepeat(bool repeat) |
707 | { |
708 | Q_D(QQuickAbstractButton); |
709 | if (d->autoRepeat == repeat) |
710 | return; |
711 | |
712 | d->stopPressRepeat(); |
713 | d->autoRepeat = repeat; |
714 | emit autoRepeatChanged(); |
715 | } |
716 | |
717 | /*! |
718 | \qmlproperty Item QtQuick.Controls::AbstractButton::indicator |
719 | |
720 | This property holds the indicator item. |
721 | */ |
722 | QQuickItem *QQuickAbstractButton::indicator() const |
723 | { |
724 | QQuickAbstractButtonPrivate *d = const_cast<QQuickAbstractButtonPrivate *>(d_func()); |
725 | if (!d->indicator) |
726 | d->executeIndicator(); |
727 | return d->indicator; |
728 | } |
729 | |
730 | void QQuickAbstractButton::setIndicator(QQuickItem *indicator) |
731 | { |
732 | Q_D(QQuickAbstractButton); |
733 | if (d->indicator == indicator) |
734 | return; |
735 | |
736 | if (!d->indicator.isExecuting()) |
737 | d->cancelIndicator(); |
738 | |
739 | const qreal oldImplicitIndicatorWidth = implicitIndicatorWidth(); |
740 | const qreal oldImplicitIndicatorHeight = implicitIndicatorHeight(); |
741 | |
742 | d->removeImplicitSizeListener(item: d->indicator); |
743 | QQuickControlPrivate::hideOldItem(item: d->indicator); |
744 | d->indicator = indicator; |
745 | |
746 | if (indicator) { |
747 | if (!indicator->parentItem()) |
748 | indicator->setParentItem(this); |
749 | indicator->setAcceptedMouseButtons(Qt::LeftButton); |
750 | d->addImplicitSizeListener(item: indicator); |
751 | } |
752 | |
753 | if (!qFuzzyCompare(p1: oldImplicitIndicatorWidth, p2: implicitIndicatorWidth())) |
754 | emit implicitIndicatorWidthChanged(); |
755 | if (!qFuzzyCompare(p1: oldImplicitIndicatorHeight, p2: implicitIndicatorHeight())) |
756 | emit implicitIndicatorHeightChanged(); |
757 | if (!d->indicator.isExecuting()) |
758 | emit indicatorChanged(); |
759 | } |
760 | |
761 | /*! |
762 | \qmlproperty string QtQuick.Controls::AbstractButton::icon.name |
763 | \qmlproperty url QtQuick.Controls::AbstractButton::icon.source |
764 | \qmlproperty int QtQuick.Controls::AbstractButton::icon.width |
765 | \qmlproperty int QtQuick.Controls::AbstractButton::icon.height |
766 | \qmlproperty color QtQuick.Controls::AbstractButton::icon.color |
767 | \qmlproperty bool QtQuick.Controls::AbstractButton::icon.cache |
768 | |
769 | This property group was added in QtQuick.Controls 2.3. |
770 | |
771 | \include qquickicon.qdocinc grouped-properties |
772 | |
773 | \sa text, display, {Icons in Qt Quick Controls} |
774 | */ |
775 | |
776 | QQuickIcon QQuickAbstractButton::icon() const |
777 | { |
778 | Q_D(const QQuickAbstractButton); |
779 | return d->effectiveIcon; |
780 | } |
781 | |
782 | void QQuickAbstractButton::setIcon(const QQuickIcon &icon) |
783 | { |
784 | Q_D(QQuickAbstractButton); |
785 | d->icon = icon; |
786 | d->updateEffectiveIcon(); |
787 | } |
788 | |
789 | /*! |
790 | \since QtQuick.Controls 2.3 (Qt 5.10) |
791 | \qmlproperty enumeration QtQuick.Controls::AbstractButton::display |
792 | |
793 | This property determines how the \l icon and \l text are displayed within |
794 | the button. |
795 | |
796 | \table |
797 | \header \li Display \li Result |
798 | \row \li \c AbstractButton.IconOnly \li \image qtquickcontrols2-button-icononly.png |
799 | \row \li \c AbstractButton.TextOnly \li \image qtquickcontrols2-button-textonly.png |
800 | \row \li \c AbstractButton.TextBesideIcon \li \image qtquickcontrols2-button-textbesideicon.png |
801 | \row \li \c AbstractButton.TextUnderIcon \li \image qtquickcontrols2-button-textundericon.png |
802 | \endtable |
803 | |
804 | \sa {Control::}{spacing}, {Control::}{padding} |
805 | */ |
806 | QQuickAbstractButton::Display QQuickAbstractButton::display() const |
807 | { |
808 | Q_D(const QQuickAbstractButton); |
809 | return d->display; |
810 | } |
811 | |
812 | void QQuickAbstractButton::setDisplay(Display display) |
813 | { |
814 | Q_D(QQuickAbstractButton); |
815 | if (display == d->display) |
816 | return; |
817 | |
818 | d->display = display; |
819 | emit displayChanged(); |
820 | } |
821 | |
822 | /*! |
823 | \since QtQuick.Controls 2.3 (Qt 5.10) |
824 | \qmlproperty Action QtQuick.Controls::AbstractButton::action |
825 | |
826 | This property holds the button action. |
827 | |
828 | \sa Action |
829 | */ |
830 | QQuickAction *QQuickAbstractButton::action() const |
831 | { |
832 | Q_D(const QQuickAbstractButton); |
833 | return d->action; |
834 | } |
835 | |
836 | void QQuickAbstractButton::setAction(QQuickAction *action) |
837 | { |
838 | Q_D(QQuickAbstractButton); |
839 | if (d->action == action) |
840 | return; |
841 | |
842 | const QString oldText = text(); |
843 | |
844 | if (QQuickAction *oldAction = d->action.data()) { |
845 | QQuickActionPrivate::get(action: oldAction)->unregisterItem(item: this); |
846 | QObjectPrivate::disconnect(sender: oldAction, signal: &QQuickAction::triggered, receiverPrivate: d, slot: &QQuickAbstractButtonPrivate::click); |
847 | QObjectPrivate::disconnect(sender: oldAction, signal: &QQuickAction::textChanged, receiverPrivate: d, slot: &QQuickAbstractButtonPrivate::actionTextChange); |
848 | |
849 | QObjectPrivate::disconnect(sender: oldAction, signal: &QQuickAction::iconChanged, receiverPrivate: d, slot: &QQuickAbstractButtonPrivate::updateEffectiveIcon); |
850 | disconnect(sender: oldAction, signal: &QQuickAction::checkedChanged, receiver: this, slot: &QQuickAbstractButton::setChecked); |
851 | disconnect(sender: oldAction, signal: &QQuickAction::checkableChanged, receiver: this, slot: &QQuickAbstractButton::setCheckable); |
852 | disconnect(sender: oldAction, signal: &QQuickAction::enabledChanged, receiver: this, slot: &QQuickItem::setEnabled); |
853 | } |
854 | |
855 | if (action) { |
856 | QQuickActionPrivate::get(action)->registerItem(item: this); |
857 | QObjectPrivate::connect(sender: action, signal: &QQuickAction::triggered, receiverPrivate: d, slot: &QQuickAbstractButtonPrivate::click); |
858 | QObjectPrivate::connect(sender: action, signal: &QQuickAction::textChanged, receiverPrivate: d, slot: &QQuickAbstractButtonPrivate::actionTextChange); |
859 | |
860 | QObjectPrivate::connect(sender: action, signal: &QQuickAction::iconChanged, receiverPrivate: d, slot: &QQuickAbstractButtonPrivate::updateEffectiveIcon); |
861 | connect(sender: action, signal: &QQuickAction::checkedChanged, receiver: this, slot: &QQuickAbstractButton::setChecked); |
862 | connect(sender: action, signal: &QQuickAction::checkableChanged, receiver: this, slot: &QQuickAbstractButton::setCheckable); |
863 | connect(sender: action, signal: &QQuickAction::enabledChanged, receiver: this, slot: &QQuickItem::setEnabled); |
864 | |
865 | setChecked(action->isChecked()); |
866 | setCheckable(action->isCheckable()); |
867 | setEnabled(action->isEnabled()); |
868 | } |
869 | |
870 | d->action = action; |
871 | |
872 | if (oldText != text()) |
873 | buttonChange(change: ButtonTextChange); |
874 | |
875 | d->updateEffectiveIcon(); |
876 | |
877 | emit actionChanged(); |
878 | } |
879 | |
880 | /*! |
881 | \since QtQuick.Controls 2.4 (Qt 5.11) |
882 | \qmlproperty int QtQuick.Controls::AbstractButton::autoRepeatDelay |
883 | |
884 | This property holds the initial delay of auto-repetition in milliseconds. |
885 | The default value is \c 300 ms. |
886 | |
887 | \sa autoRepeat, autoRepeatInterval |
888 | */ |
889 | int QQuickAbstractButton::autoRepeatDelay() const |
890 | { |
891 | Q_D(const QQuickAbstractButton); |
892 | return d->repeatDelay; |
893 | } |
894 | |
895 | void QQuickAbstractButton::setAutoRepeatDelay(int delay) |
896 | { |
897 | Q_D(QQuickAbstractButton); |
898 | if (d->repeatDelay == delay) |
899 | return; |
900 | |
901 | d->repeatDelay = delay; |
902 | emit autoRepeatDelayChanged(); |
903 | } |
904 | |
905 | /*! |
906 | \since QtQuick.Controls 2.4 (Qt 5.11) |
907 | \qmlproperty int QtQuick.Controls::AbstractButton::autoRepeatInterval |
908 | |
909 | This property holds the interval of auto-repetition in milliseconds. |
910 | The default value is \c 100 ms. |
911 | |
912 | \sa autoRepeat, autoRepeatDelay |
913 | */ |
914 | int QQuickAbstractButton::autoRepeatInterval() const |
915 | { |
916 | Q_D(const QQuickAbstractButton); |
917 | return d->repeatInterval; |
918 | } |
919 | |
920 | void QQuickAbstractButton::setAutoRepeatInterval(int interval) |
921 | { |
922 | Q_D(QQuickAbstractButton); |
923 | if (d->repeatInterval == interval) |
924 | return; |
925 | |
926 | d->repeatInterval = interval; |
927 | emit autoRepeatIntervalChanged(); |
928 | } |
929 | |
930 | #if QT_CONFIG(shortcut) |
931 | QKeySequence QQuickAbstractButton::shortcut() const |
932 | { |
933 | Q_D(const QQuickAbstractButton); |
934 | return d->shortcut; |
935 | } |
936 | |
937 | void QQuickAbstractButton::setShortcut(const QKeySequence &shortcut) |
938 | { |
939 | Q_D(QQuickAbstractButton); |
940 | if (d->shortcut == shortcut) |
941 | return; |
942 | |
943 | d->ungrabShortcut(); |
944 | d->shortcut = shortcut; |
945 | if (isVisible()) |
946 | d->grabShortcut(); |
947 | } |
948 | #endif |
949 | |
950 | /*! |
951 | \readonly |
952 | \since QtQuick.Controls 2.4 (Qt 5.11) |
953 | \qmlproperty real QtQuick.Controls::AbstractButton::pressX |
954 | |
955 | This property holds the x-coordinate of the last press. |
956 | |
957 | \note The value is updated on touch moves, but left intact after touch release. |
958 | |
959 | \sa pressY |
960 | */ |
961 | qreal QQuickAbstractButton::pressX() const |
962 | { |
963 | Q_D(const QQuickAbstractButton); |
964 | return d->movePoint.x(); |
965 | } |
966 | |
967 | /*! |
968 | \readonly |
969 | \since QtQuick.Controls 2.4 (Qt 5.11) |
970 | \qmlproperty real QtQuick.Controls::AbstractButton::pressY |
971 | |
972 | This property holds the y-coordinate of the last press. |
973 | |
974 | \note The value is updated on touch moves, but left intact after touch release. |
975 | |
976 | \sa pressX |
977 | */ |
978 | qreal QQuickAbstractButton::pressY() const |
979 | { |
980 | Q_D(const QQuickAbstractButton); |
981 | return d->movePoint.y(); |
982 | } |
983 | |
984 | /*! |
985 | \since QtQuick.Controls 2.5 (Qt 5.12) |
986 | \qmlproperty real QtQuick.Controls::AbstractButton::implicitIndicatorWidth |
987 | \readonly |
988 | |
989 | This property holds the implicit indicator width. |
990 | |
991 | The value is equal to \c {indicator ? indicator.implicitWidth : 0}. |
992 | |
993 | This is typically used, together with \l {Control::}{implicitContentWidth} and |
994 | \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}. |
995 | |
996 | \sa implicitIndicatorHeight |
997 | */ |
998 | qreal QQuickAbstractButton::implicitIndicatorWidth() const |
999 | { |
1000 | Q_D(const QQuickAbstractButton); |
1001 | if (!d->indicator) |
1002 | return 0; |
1003 | return d->indicator->implicitWidth(); |
1004 | } |
1005 | |
1006 | /*! |
1007 | \since QtQuick.Controls 2.5 (Qt 5.12) |
1008 | \qmlproperty real QtQuick.Controls::AbstractButton::implicitIndicatorHeight |
1009 | \readonly |
1010 | |
1011 | This property holds the implicit indicator height. |
1012 | |
1013 | The value is equal to \c {indicator ? indicator.implicitHeight : 0}. |
1014 | |
1015 | This is typically used, together with \l {Control::}{implicitContentHeight} and |
1016 | \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}. |
1017 | |
1018 | \sa implicitIndicatorWidth |
1019 | */ |
1020 | qreal QQuickAbstractButton::implicitIndicatorHeight() const |
1021 | { |
1022 | Q_D(const QQuickAbstractButton); |
1023 | if (!d->indicator) |
1024 | return 0; |
1025 | return d->indicator->implicitHeight(); |
1026 | } |
1027 | |
1028 | /*! |
1029 | \qmlmethod void QtQuick.Controls::AbstractButton::toggle() |
1030 | |
1031 | Toggles the checked state of the button. |
1032 | */ |
1033 | void QQuickAbstractButton::toggle() |
1034 | { |
1035 | Q_D(QQuickAbstractButton); |
1036 | setChecked(!d->checked); |
1037 | } |
1038 | |
1039 | void QQuickAbstractButton::componentComplete() |
1040 | { |
1041 | Q_D(QQuickAbstractButton); |
1042 | d->executeIndicator(complete: true); |
1043 | QQuickControl::componentComplete(); |
1044 | } |
1045 | |
1046 | bool QQuickAbstractButton::event(QEvent *event) |
1047 | { |
1048 | #if QT_CONFIG(shortcut) |
1049 | Q_D(QQuickAbstractButton); |
1050 | if (event->type() == QEvent::Shortcut) { |
1051 | QShortcutEvent *se = static_cast<QShortcutEvent *>(event); |
1052 | if (se->shortcutId() == d->shortcutId) { |
1053 | d->trigger(); |
1054 | return true; |
1055 | } |
1056 | } |
1057 | #endif |
1058 | return QQuickControl::event(event); |
1059 | } |
1060 | |
1061 | void QQuickAbstractButton::focusOutEvent(QFocusEvent *event) |
1062 | { |
1063 | Q_D(QQuickAbstractButton); |
1064 | QQuickControl::focusOutEvent(event); |
1065 | if (d->touchId == -1) // don't ungrab on multi-touch if another control gets focused |
1066 | d->handleUngrab(); |
1067 | } |
1068 | |
1069 | void QQuickAbstractButton::keyPressEvent(QKeyEvent *event) |
1070 | { |
1071 | Q_D(QQuickAbstractButton); |
1072 | QQuickControl::keyPressEvent(event); |
1073 | if (d->acceptKeyClick(key: static_cast<Qt::Key>(event->key()))) { |
1074 | d->setPressPoint(QPoint(qRound(d: width() / 2), qRound(d: height() / 2))); |
1075 | setPressed(true); |
1076 | |
1077 | if (d->autoRepeat) |
1078 | d->startRepeatDelay(); |
1079 | |
1080 | emit pressed(); |
1081 | event->accept(); |
1082 | } |
1083 | } |
1084 | |
1085 | void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event) |
1086 | { |
1087 | Q_D(QQuickAbstractButton); |
1088 | QQuickControl::keyReleaseEvent(event); |
1089 | if (d->pressed && d->acceptKeyClick(key: static_cast<Qt::Key>(event->key()))) { |
1090 | setPressed(false); |
1091 | |
1092 | nextCheckState(); |
1093 | emit released(); |
1094 | d->trigger(); |
1095 | |
1096 | if (d->autoRepeat) |
1097 | d->stopPressRepeat(); |
1098 | event->accept(); |
1099 | } |
1100 | } |
1101 | |
1102 | void QQuickAbstractButton::mousePressEvent(QMouseEvent *event) |
1103 | { |
1104 | Q_D(QQuickAbstractButton); |
1105 | d->pressButtons = event->buttons(); |
1106 | QQuickControl::mousePressEvent(event); |
1107 | } |
1108 | |
1109 | void QQuickAbstractButton::mouseDoubleClickEvent(QMouseEvent *event) |
1110 | { |
1111 | Q_D(QQuickAbstractButton); |
1112 | if (d->isDoubleClickConnected()) { |
1113 | QQuickControl::mouseDoubleClickEvent(event); |
1114 | emit doubleClicked(); |
1115 | d->wasDoubleClick = true; |
1116 | } |
1117 | } |
1118 | |
1119 | void QQuickAbstractButton::timerEvent(QTimerEvent *event) |
1120 | { |
1121 | Q_D(QQuickAbstractButton); |
1122 | QQuickControl::timerEvent(event); |
1123 | if (event->timerId() == d->holdTimer) { |
1124 | d->stopPressAndHold(); |
1125 | d->wasHeld = true; |
1126 | emit pressAndHold(); |
1127 | } else if (event->timerId() == d->delayTimer) { |
1128 | d->startPressRepeat(); |
1129 | } else if (event->timerId() == d->repeatTimer) { |
1130 | emit released(); |
1131 | d->trigger(); |
1132 | emit pressed(); |
1133 | } |
1134 | } |
1135 | |
1136 | void QQuickAbstractButton::itemChange(ItemChange change, const ItemChangeData &value) |
1137 | { |
1138 | QQuickControl::itemChange(change, value); |
1139 | #if QT_CONFIG(shortcut) |
1140 | Q_D(QQuickAbstractButton); |
1141 | if (change == ItemVisibleHasChanged) { |
1142 | if (value.boolValue) |
1143 | d->grabShortcut(); |
1144 | else |
1145 | d->ungrabShortcut(); |
1146 | } |
1147 | #endif |
1148 | } |
1149 | |
1150 | void QQuickAbstractButton::buttonChange(ButtonChange change) |
1151 | { |
1152 | Q_D(QQuickAbstractButton); |
1153 | switch (change) { |
1154 | case ButtonCheckedChange: |
1155 | if (d->checked) { |
1156 | QQuickAbstractButton *button = d->findCheckedButton(); |
1157 | if (button && button != this) |
1158 | button->setChecked(false); |
1159 | } |
1160 | break; |
1161 | case ButtonTextChange: { |
1162 | const QString txt = text(); |
1163 | maybeSetAccessibleName(name: txt); |
1164 | #if QT_CONFIG(shortcut) |
1165 | setShortcut(QKeySequence::mnemonic(text: txt)); |
1166 | #endif |
1167 | emit textChanged(); |
1168 | break; |
1169 | } |
1170 | default: |
1171 | break; |
1172 | } |
1173 | } |
1174 | |
1175 | void QQuickAbstractButton::nextCheckState() |
1176 | { |
1177 | Q_D(QQuickAbstractButton); |
1178 | if (d->checkable && (!d->checked || d->findCheckedButton() != this)) |
1179 | d->toggle(value: !d->checked); |
1180 | } |
1181 | |
1182 | #if QT_CONFIG(accessibility) |
1183 | void QQuickAbstractButton::accessibilityActiveChanged(bool active) |
1184 | { |
1185 | QQuickControl::accessibilityActiveChanged(active); |
1186 | |
1187 | Q_D(QQuickAbstractButton); |
1188 | if (active) { |
1189 | maybeSetAccessibleName(name: text()); |
1190 | setAccessibleProperty(propertyName: "pressed" , value: d->pressed); |
1191 | setAccessibleProperty(propertyName: "checked" , value: d->checked); |
1192 | setAccessibleProperty(propertyName: "checkable" , value: d->checkable); |
1193 | } |
1194 | } |
1195 | |
1196 | QAccessible::Role QQuickAbstractButton::accessibleRole() const |
1197 | { |
1198 | Q_D(const QQuickAbstractButton); |
1199 | if (d->checkable) { |
1200 | return QAccessible::CheckBox; |
1201 | } |
1202 | return QAccessible::Button; |
1203 | } |
1204 | #endif |
1205 | |
1206 | QT_END_NAMESPACE |
1207 | |