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