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 "qquickdialogbuttonbox_p.h"
5#include "qquickdialogbuttonbox_p_p.h"
6#include "qquickabstractbutton_p.h"
7#include "qquickbutton_p.h"
8#include "qquickdialog_p_p.h"
9
10#include <QtCore/qpointer.h>
11#include <QtGui/private/qguiapplication_p.h>
12#include <QtGui/qpa/qplatformtheme.h>
13#include <QtQml/qqmlengine.h>
14#include <QtQml/qqmlcontext.h>
15#include <QtQml/qqmlcomponent.h>
16
17#include <algorithm>
18
19QT_BEGIN_NAMESPACE
20
21/*!
22 \qmltype DialogButtonBox
23 \inherits Container
24//! \nativetype QQuickDialogButtonBox
25 \inqmlmodule QtQuick.Controls
26 \ingroup qtquickcontrols-dialogs
27 \brief A button box used in dialogs.
28 \since 5.8
29
30 Dialogs and message boxes typically present buttons in an order that
31 conforms to the interface guidelines for that platform. Invariably,
32 different platforms have their dialog buttons in different orders.
33 DialogButtonBox allows a developer to add buttons to it and will
34 automatically use the appropriate order for the user's platform.
35
36 Most buttons for a dialog follow certain roles. Such roles include:
37
38 \list
39 \li Accepting or rejecting the dialog.
40 \li Asking for help.
41 \li Performing actions on the dialog itself (such as resetting fields or
42 applying changes).
43 \endlist
44
45 There can also be alternate ways of dismissing the dialog which may cause
46 destructive results.
47
48 Most dialogs have buttons that can almost be considered standard (e.g.
49 \uicontrol OK and \uicontrol Cancel buttons). It is sometimes convenient
50 to create these buttons in a standard way.
51
52 There are a couple ways of using DialogButtonBox. One way is to specify
53 the standard buttons (e.g. \uicontrol OK, \uicontrol Cancel, \uicontrol Save)
54 and let the button box setup the buttons.
55
56 \image qtquickcontrols-dialogbuttonbox.png
57
58 \snippet qtquickcontrols-dialogbuttonbox.qml 1
59
60 Alternatively, buttons and their roles can be specified by hand:
61
62 \snippet qtquickcontrols-dialogbuttonbox-attached.qml 1
63
64 You can also mix and match normal buttons and standard buttons.
65
66 When a button is clicked in the button box, the \l clicked() signal is
67 emitted for the actual button that is pressed. In addition, the
68 following signals are automatically emitted when a button with the
69 respective role(s) is pressed:
70
71 \table
72 \header
73 \li Role
74 \li Signal
75 \row
76 \li \c AcceptRole, \c YesRole
77 \li \l accepted()
78 \row
79 \li \c ApplyRole
80 \li \l applied()
81 \row
82 \li \c DiscardRole
83 \li \l discarded()
84 \row
85 \li \c HelpRole
86 \li \l helpRequested()
87 \row
88 \li \c RejectRole, \c NoRole
89 \li \l rejected()
90 \row
91 \li \c ResetRole
92 \li \l reset()
93 \endtable
94
95 \sa Dialog
96*/
97
98/*!
99 \qmlsignal QtQuick.Controls::DialogButtonBox::accepted()
100
101 This signal is emitted when a button defined with the \c AcceptRole or
102 \c YesRole is clicked.
103
104 \sa rejected(), clicked(), helpRequested()
105*/
106
107/*!
108 \qmlsignal QtQuick.Controls::DialogButtonBox::rejected()
109
110 This signal is emitted when a button defined with the \c RejectRole or
111 \c NoRole is clicked.
112
113 \sa accepted(), helpRequested(), clicked()
114*/
115
116/*!
117 \since QtQuick.Controls 2.3 (Qt 5.10)
118 \qmlsignal QtQuick.Controls::DialogButtonBox::applied()
119
120 This signal is emitted when a button defined with the \c ApplyRole is
121 clicked.
122
123 \sa discarded(), reset()
124*/
125
126/*!
127 \since QtQuick.Controls 2.3 (Qt 5.10)
128 \qmlsignal QtQuick.Controls::DialogButtonBox::reset()
129
130 This signal is emitted when a button defined with the \c ResetRole is
131 clicked.
132
133 \sa discarded(), applied()
134*/
135
136/*!
137 \since QtQuick.Controls 2.3 (Qt 5.10)
138 \qmlsignal QtQuick.Controls::DialogButtonBox::discarded()
139
140 This signal is emitted when a button defined with the \c DiscardRole is
141 clicked.
142
143 \sa reset(), applied()
144*/
145
146/*!
147 \qmlsignal QtQuick.Controls::DialogButtonBox::helpRequested()
148
149 This signal is emitted when a button defined with the \c HelpRole is clicked.
150
151 \sa accepted(), rejected(), clicked()
152*/
153
154/*!
155 \qmlsignal QtQuick.Controls::DialogButtonBox::clicked(AbstractButton button)
156
157 This signal is emitted when a \a button inside the button box is clicked.
158
159 \sa accepted(), rejected(), helpRequested()
160*/
161
162static QPlatformDialogHelper::ButtonLayout platformButtonLayout()
163{
164 return QGuiApplicationPrivate::platformTheme()->themeHint(hint: QPlatformTheme::DialogButtonBoxLayout).value<QPlatformDialogHelper::ButtonLayout>();
165}
166
167void QQuickDialogButtonBoxPrivate::itemImplicitWidthChanged(QQuickItem *item)
168{
169 QQuickContainerPrivate::itemImplicitWidthChanged(item);
170 if (item == contentItem)
171 resizeContent();
172 else
173 updateImplicitContentWidth();
174}
175
176void QQuickDialogButtonBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
177{
178 QQuickContainerPrivate::itemImplicitHeightChanged(item);
179 if (item == contentItem)
180 resizeContent();
181 else
182 updateImplicitContentHeight();
183}
184
185// adapted from QStyle::alignedRect()
186static QRectF alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSizeF &size, const QRectF &rectangle)
187{
188 alignment = QGuiApplicationPrivate::visualAlignment(direction, alignment);
189 qreal x = rectangle.x();
190 qreal y = rectangle.y();
191 qreal w = size.width();
192 qreal h = size.height();
193 // The qMax() calls ensure that the x and y values are never negative, which can happen if the contentWidth is smaller than the width.
194 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter || (alignment & Qt::AlignVertical_Mask) == 0)
195 y += qMax<qreal>(a: 0, b: (rectangle.size().height() - h) / 2);
196 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
197 y += qMax<qreal>(a: 0, b: rectangle.size().height() - h);
198 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
199 x += qMax<qreal>(a: 0, b: rectangle.size().width() - w);
200 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
201 x += qMax<qreal>(a: 0, b: (rectangle.size().width() - w) / 2);
202 return QRectF(x, y, w, h);
203}
204
205void QQuickDialogButtonBoxPrivate::resizeContent()
206{
207 Q_Q(QQuickDialogButtonBox);
208 if (!contentItem || !contentModel)
209 return;
210
211 QRectF geometry = q->boundingRect().adjusted(xp1: q->leftPadding(), yp1: q->topPadding(), xp2: -q->rightPadding(), yp2: -q->bottomPadding());
212 if (alignment != 0)
213 geometry = alignedRect(direction: q->isMirrored() ? Qt::RightToLeft : Qt::LeftToRight, alignment, size: QSizeF(contentWidth, contentHeight), rectangle: geometry);
214
215 contentItem->setPosition(geometry.topLeft());
216 contentItem->setSize(geometry.size());
217}
218
219void QQuickDialogButtonBoxPrivate::updateLayout()
220{
221 Q_Q(QQuickDialogButtonBox);
222 const int count = contentModel->count();
223 if (count <= 0)
224 return;
225
226 const int halign = alignment & Qt::AlignHorizontal_Mask;
227 const int valign = alignment & Qt::AlignVertical_Mask;
228
229 QList<QQuickAbstractButton *> buttons;
230 const qreal cw = (alignment & Qt::AlignHorizontal_Mask) == 0 ? q->availableWidth() : contentWidth;
231 const qreal itemWidth = (cw - qMax(a: 0, b: count - 1) * spacing) / count;
232
233 for (int i = 0; i < count; ++i) {
234 QQuickItem *item = q->itemAt(index: i);
235 if (item) {
236 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
237 if (!p->widthValid()) {
238 if (!halign)
239 item->setWidth(itemWidth);
240 else
241 item->resetWidth();
242 if (!valign)
243 item->setHeight(contentHeight);
244 else
245 item->resetHeight();
246 p->widthValidFlag = false;
247 }
248 }
249 buttons += static_cast<QQuickAbstractButton *>(item);
250 }
251
252 struct ButtonLayout {
253 ButtonLayout(QPlatformDialogHelper::ButtonLayout layout)
254 : m_layout(QPlatformDialogHelper::buttonLayout(orientation: Qt::Horizontal, policy: layout))
255 {
256 }
257
258 bool operator()(QQuickAbstractButton *first, QQuickAbstractButton *second)
259 {
260 const QPlatformDialogHelper::ButtonRole firstRole = QQuickDialogPrivate::buttonRole(button: first);
261 const QPlatformDialogHelper::ButtonRole secondRole = QQuickDialogPrivate::buttonRole(button: second);
262
263 if (firstRole != secondRole && firstRole != QPlatformDialogHelper::InvalidRole && secondRole != QPlatformDialogHelper::InvalidRole) {
264 const int *l = m_layout;
265 while (*l != QPlatformDialogHelper::EOL) {
266 // Unset the Reverse flag.
267 const int role = (*l & ~QPlatformDialogHelper::Reverse);
268 if (role == firstRole)
269 return true;
270 if (role == secondRole)
271 return false;
272 ++l;
273 }
274 }
275
276 if (firstRole == secondRole)
277 return false;
278
279 return firstRole != QPlatformDialogHelper::InvalidRole;
280 }
281 const int *m_layout;
282 };
283
284 std::stable_sort(first: buttons.begin(), last: buttons.end(), comp: ButtonLayout(static_cast<QPlatformDialogHelper::ButtonLayout>(buttonLayout)));
285
286 for (int i = 0; i < buttons.size() - 1; ++i)
287 q->insertItem(index: i, item: buttons.at(i));
288}
289
290qreal QQuickDialogButtonBoxPrivate::getContentWidth() const
291{
292 Q_Q(const QQuickDialogButtonBox);
293 if (!contentModel)
294 return 0;
295
296 const int count = contentModel->count();
297 const qreal totalSpacing = qMax(a: 0, b: count - 1) * spacing;
298 qreal totalWidth = totalSpacing;
299 qreal maxWidth = 0;
300 for (int i = 0; i < count; ++i) {
301 QQuickItem *item = q->itemAt(index: i);
302 if (item) {
303 totalWidth += item->implicitWidth();
304 maxWidth = qMax(a: maxWidth, b: item->implicitWidth());
305 }
306 }
307 if ((alignment & Qt::AlignHorizontal_Mask) == 0)
308 totalWidth = qMax(a: totalWidth, b: count * maxWidth + totalSpacing);
309 return totalWidth;
310}
311
312qreal QQuickDialogButtonBoxPrivate::getContentHeight() const
313{
314 Q_Q(const QQuickDialogButtonBox);
315 if (!contentModel)
316 return 0;
317
318 const int count = contentModel->count();
319 qreal maxHeight = 0;
320 for (int i = 0; i < count; ++i) {
321 QQuickItem *item = q->itemAt(index: i);
322 if (item)
323 maxHeight = qMax(a: maxHeight, b: item->implicitHeight());
324 }
325 return maxHeight;
326}
327
328void QQuickDialogButtonBoxPrivate::handleClick()
329{
330 Q_Q(QQuickDialogButtonBox);
331 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: q->sender());
332 if (!button)
333 return;
334
335 // Can't fetch this *after* emitting clicked, as clicked may destroy the button
336 // or change its role. Now changing the role is not possible yet, but arguably
337 // both clicked and accepted/rejected/etc. should be emitted "atomically"
338 // depending on whatever role the button had at the time of the click.
339 const QPlatformDialogHelper::ButtonRole role = QQuickDialogPrivate::buttonRole(button);
340 QPointer<QQuickDialogButtonBox> guard(q);
341
342 emit q->clicked(button);
343
344 if (!guard)
345 return;
346
347 switch (role) {
348 case QPlatformDialogHelper::AcceptRole:
349 case QPlatformDialogHelper::YesRole:
350 emit q->accepted();
351 break;
352 case QPlatformDialogHelper::RejectRole:
353 case QPlatformDialogHelper::NoRole:
354 emit q->rejected();
355 break;
356 case QPlatformDialogHelper::ApplyRole:
357 emit q->applied();
358 break;
359 case QPlatformDialogHelper::ResetRole:
360 emit q->reset();
361 break;
362 case QPlatformDialogHelper::DestructiveRole:
363 emit q->discarded();
364 break;
365 case QPlatformDialogHelper::HelpRole:
366 emit q->helpRequested();
367 break;
368 default:
369 break;
370 }
371}
372
373QString QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::StandardButton standardButton)
374{
375 return QPlatformTheme::removeMnemonics(original: QGuiApplicationPrivate::platformTheme()->standardButtonText(button: standardButton));
376}
377
378QQuickAbstractButton *QQuickDialogButtonBoxPrivate::createStandardButton(QPlatformDialogHelper::StandardButton standardButton)
379{
380 Q_Q(QQuickDialogButtonBox);
381 if (!delegate)
382 return nullptr;
383
384 QQmlContext *creationContext = delegate->creationContext();
385 if (!creationContext)
386 creationContext = qmlContext(q);
387
388 QObject *object = delegate->beginCreate(creationContext);
389 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(object);
390 if (button) {
391 QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: button, create: true));
392 QQuickDialogButtonBoxAttachedPrivate::get(q: attached)->standardButton = standardButton;
393 attached->setButtonRole(QPlatformDialogHelper::buttonRole(button: standardButton));
394 button->setText(buttonText(standardButton));
395 delegate->completeCreate();
396 button->setParent(q);
397 return button;
398 }
399
400 delete object;
401 return nullptr;
402}
403
404void QQuickDialogButtonBoxPrivate::removeStandardButtons()
405{
406 Q_Q(QQuickDialogButtonBox);
407 int i = q->count() - 1;
408 while (i >= 0) {
409 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: q->itemAt(index: i));
410 if (button) {
411 QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(
412 object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: button, create: false));
413 if (attached) {
414 QQuickDialogButtonBoxAttachedPrivate *p = QQuickDialogButtonBoxAttachedPrivate::get(q: attached);
415 if (p->standardButton != QPlatformDialogHelper::NoButton) {
416 q->removeItem(item: button);
417 button->deleteLater();
418 }
419 }
420 }
421 --i;
422 }
423}
424
425void QQuickDialogButtonBoxPrivate::updateLanguage()
426{
427 Q_Q(QQuickDialogButtonBox);
428 int i = q->count() - 1;
429 while (i >= 0) {
430 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: itemAt(index: i));
431 if (button) {
432 QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(
433 object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: button, create: true));
434 const auto boxAttachedPrivate = QQuickDialogButtonBoxAttachedPrivate::get(q: attached);
435 const QPlatformDialogHelper::StandardButton standardButton = boxAttachedPrivate->standardButton;
436 // The button might be a custom one with explicitly specified text, so we shouldn't change it in that case.
437 if (standardButton != QPlatformDialogHelper::NoButton) {
438 button->setText(buttonText(standardButton));
439 }
440 }
441 --i;
442 }
443}
444
445QPlatformDialogHelper::StandardButton QQuickDialogButtonBoxPrivate::standardButton(QQuickAbstractButton *button) const {
446 QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: button, create: false));
447 if (attached)
448 return QQuickDialogButtonBoxAttachedPrivate::get(q: attached)->standardButton;
449 else
450 return QPlatformDialogHelper::NoButton;
451}
452
453QQuickDialogButtonBox::QQuickDialogButtonBox(QQuickItem *parent)
454 : QQuickContainer(*(new QQuickDialogButtonBoxPrivate), parent)
455{
456 Q_D(QQuickDialogButtonBox);
457 d->changeTypes |= QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
458 d->buttonLayout = platformButtonLayout();
459 d->setSizePolicy(horizontalPolicy: QLayoutPolicy::Preferred, verticalPolicy: QLayoutPolicy::Fixed);
460}
461
462QQuickDialogButtonBox::~QQuickDialogButtonBox()
463{
464 Q_D(QQuickDialogButtonBox);
465 // QQuickContainerPrivate does call this, but as our type information has already been
466 // destroyed by that point (since this destructor has already run), it won't call our
467 // implementation. So, we need to make sure our implementation is called. If we don't do this,
468 // the listener we installed on the contentItem won't get removed, possibly resulting in
469 // heap-use-after-frees.
470 contentItemChange(newItem: nullptr, oldItem: d->contentItem);
471}
472
473/*!
474 \qmlproperty enumeration QtQuick.Controls::DialogButtonBox::position
475
476 This property holds the position of the button box.
477
478 \note If the button box is assigned as a header or footer of ApplicationWindow
479 or Page, the appropriate position is set automatically.
480
481 Possible values:
482 \value DialogButtonBox.Header The button box is at the top, as a window or page header.
483 \value DialogButtonBox.Footer The button box is at the bottom, as a window or page footer.
484
485 The default value is \c Footer.
486
487 \sa Dialog::header, Dialog::footer
488*/
489QQuickDialogButtonBox::Position QQuickDialogButtonBox::position() const
490{
491 Q_D(const QQuickDialogButtonBox);
492 return d->position;
493}
494
495void QQuickDialogButtonBox::setPosition(Position position)
496{
497 Q_D(QQuickDialogButtonBox);
498 if (d->position == position)
499 return;
500
501 d->position = position;
502 emit positionChanged();
503}
504
505/*!
506 \qmlproperty flags QtQuick.Controls::DialogButtonBox::alignment
507
508 This property holds the alignment of the buttons.
509
510 Possible values:
511 \value Qt.AlignLeft The buttons are aligned to the left.
512 \value Qt.AlignHCenter The buttons are horizontally centered.
513 \value Qt.AlignRight The buttons are aligned to the right.
514 \value Qt.AlignTop The buttons are aligned to the top.
515 \value Qt.AlignVCenter The buttons are vertically centered.
516 \value Qt.AlignBottom The buttons are aligned to the bottom.
517
518 By default, no specific alignment is set; reading the alignment property yields
519 a default flag value which compares equal to 0. The property can be reset to this
520 value by assigning \c{undefined} to it. In this case, the buttons are resized to
521 fill the available space.
522
523 \note This property assumes a horizontal layout of the buttons.
524 Note that when running the \l {iOS Style}{iOS style}, the DialogButtonBox will use
525 a vertical layout if this property is set to anything other than \c undefined and
526 there are more than two buttons.
527 In all other cases, the buttons will be arranged horizontally.
528*/
529Qt::Alignment QQuickDialogButtonBox::alignment() const
530{
531 Q_D(const QQuickDialogButtonBox);
532 return d->alignment;
533}
534
535void QQuickDialogButtonBox::setAlignment(Qt::Alignment alignment)
536{
537 Q_D(QQuickDialogButtonBox);
538 if (d->alignment == alignment)
539 return;
540
541 d->alignment = alignment;
542 if (isComponentComplete()) {
543 d->resizeContent();
544 polish();
545 }
546 emit alignmentChanged();
547}
548
549void QQuickDialogButtonBox::resetAlignment()
550{
551 setAlignment({});
552}
553
554/*!
555 \qmlproperty enumeration QtQuick.Controls::DialogButtonBox::standardButtons
556
557 This property holds a combination of standard buttons that are used by the button box.
558
559 \snippet qtquickcontrols-dialogbuttonbox.qml 1
560
561 The buttons will be positioned in the appropriate order for the user's platform.
562
563 Possible flags:
564 \value DialogButtonBox.Ok An "OK" button defined with the \c AcceptRole.
565 \value DialogButtonBox.Open An "Open" button defined with the \c AcceptRole.
566 \value DialogButtonBox.Save A "Save" button defined with the \c AcceptRole.
567 \value DialogButtonBox.Cancel A "Cancel" button defined with the \c RejectRole.
568 \value DialogButtonBox.Close A "Close" button defined with the \c RejectRole.
569 \value DialogButtonBox.Discard A "Discard" or "Don't Save" button, depending on the platform, defined with the \c DestructiveRole.
570 \value DialogButtonBox.Apply An "Apply" button defined with the \c ApplyRole.
571 \value DialogButtonBox.Reset A "Reset" button defined with the \c ResetRole.
572 \value DialogButtonBox.RestoreDefaults A "Restore Defaults" button defined with the \c ResetRole.
573 \value DialogButtonBox.Help A "Help" button defined with the \c HelpRole.
574 \value DialogButtonBox.SaveAll A "Save All" button defined with the \c AcceptRole.
575 \value DialogButtonBox.Yes A "Yes" button defined with the \c YesRole.
576 \value DialogButtonBox.YesToAll A "Yes to All" button defined with the \c YesRole.
577 \value DialogButtonBox.No A "No" button defined with the \c NoRole.
578 \value DialogButtonBox.NoToAll A "No to All" button defined with the \c NoRole.
579 \value DialogButtonBox.Abort An "Abort" button defined with the \c RejectRole.
580 \value DialogButtonBox.Retry A "Retry" button defined with the \c AcceptRole.
581 \value DialogButtonBox.Ignore An "Ignore" button defined with the \c AcceptRole.
582 \value DialogButtonBox.NoButton An invalid button.
583
584 \sa standardButton()
585*/
586QPlatformDialogHelper::StandardButtons QQuickDialogButtonBox::standardButtons() const
587{
588 Q_D(const QQuickDialogButtonBox);
589 return d->standardButtons;
590}
591
592void QQuickDialogButtonBox::setStandardButtons(QPlatformDialogHelper::StandardButtons buttons)
593{
594 Q_D(QQuickDialogButtonBox);
595 if (d->standardButtons == buttons)
596 return;
597
598 d->removeStandardButtons();
599
600 for (int i = QPlatformDialogHelper::FirstButton; i <= QPlatformDialogHelper::LastButton; i<<=1) {
601 QPlatformDialogHelper::StandardButton standardButton = static_cast<QPlatformDialogHelper::StandardButton>(i);
602 if (standardButton & buttons) {
603 QQuickAbstractButton *button = d->createStandardButton(standardButton);
604 if (button)
605 addItem(item: button);
606 }
607 }
608
609 if (isComponentComplete())
610 polish();
611
612 d->standardButtons = buttons;
613 emit standardButtonsChanged();
614}
615
616/*!
617 \qmlmethod AbstractButton QtQuick.Controls::DialogButtonBox::standardButton(StandardButton button)
618
619 Returns the specified standard \a button, or \c null if it does not exist.
620
621 \sa standardButtons
622*/
623QQuickAbstractButton *QQuickDialogButtonBox::standardButton(QPlatformDialogHelper::StandardButton button) const
624{
625 Q_D(const QQuickDialogButtonBox);
626 if (Q_UNLIKELY(!(d->standardButtons & button)))
627 return nullptr;
628 for (int i = 0, n = count(); i < n; ++i) {
629 QQuickAbstractButton *btn = qobject_cast<QQuickAbstractButton *>(object: d->itemAt(index: i));
630 if (Q_LIKELY(btn)) {
631 QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: btn, create: false));
632 if (attached && QQuickDialogButtonBoxAttachedPrivate::get(q: attached)->standardButton == button)
633 return btn;
634 }
635 }
636 return nullptr;
637}
638
639/*!
640 \qmlproperty Component QtQuick.Controls::DialogButtonBox::delegate
641
642 This property holds a delegate for creating standard buttons.
643
644 \sa standardButtons
645*/
646QQmlComponent *QQuickDialogButtonBox::delegate() const
647{
648 Q_D(const QQuickDialogButtonBox);
649 return d->delegate;
650}
651
652void QQuickDialogButtonBox::setDelegate(QQmlComponent* delegate)
653{
654 Q_D(QQuickDialogButtonBox);
655 if (d->delegate == delegate)
656 return;
657
658 delete d->delegate;
659 d->delegate = delegate;
660 emit delegateChanged();
661}
662
663QQuickDialogButtonBoxAttached *QQuickDialogButtonBox::qmlAttachedProperties(QObject *object)
664{
665 return new QQuickDialogButtonBoxAttached(object);
666}
667
668/*!
669 \since QtQuick.Controls 2.5 (Qt 5.12)
670 \qmlproperty enumeration QtQuick.Controls::DialogButtonBox::buttonLayout
671
672 This property holds the button layout policy to be used when arranging the buttons contained in the button box.
673 The default value is platform-specific.
674
675 Available values:
676 \value DialogButtonBox.WinLayout Use a policy appropriate for applications on Windows.
677 \value DialogButtonBox.MacLayout Use a policy appropriate for applications on macOS.
678 \value DialogButtonBox.KdeLayout Use a policy appropriate for applications on KDE.
679 \value DialogButtonBox.GnomeLayout Use a policy appropriate for applications on GNOME.
680 \value DialogButtonBox.AndroidLayout Use a policy appropriate for applications on Android.
681*/
682QPlatformDialogHelper::ButtonLayout QQuickDialogButtonBox::buttonLayout() const
683{
684 Q_D(const QQuickDialogButtonBox);
685 return d->buttonLayout;
686}
687
688void QQuickDialogButtonBox::setButtonLayout(QPlatformDialogHelper::ButtonLayout layout)
689{
690 Q_D(QQuickDialogButtonBox);
691 if (d->buttonLayout == layout)
692 return;
693
694 d->buttonLayout = layout;
695 if (isComponentComplete())
696 d->updateLayout();
697 emit buttonLayoutChanged();
698}
699
700void QQuickDialogButtonBox::resetButtonLayout()
701{
702 setButtonLayout(platformButtonLayout());
703}
704
705void QQuickDialogButtonBox::updatePolish()
706{
707 Q_D(QQuickDialogButtonBox);
708 QQuickContainer::updatePolish();
709 d->updateLayout();
710}
711
712bool QQuickDialogButtonBox::event(QEvent *e)
713{
714 Q_D(QQuickDialogButtonBox);
715 if (e->type() == QEvent::LanguageChange)
716 d->updateLanguage();
717 return QQuickContainer::event(e);
718}
719
720void QQuickDialogButtonBox::componentComplete()
721{
722 Q_D(QQuickDialogButtonBox);
723 QQuickContainer::componentComplete();
724 d->updateLayout();
725}
726
727void QQuickDialogButtonBox::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
728{
729 Q_D(QQuickDialogButtonBox);
730 QQuickContainer::geometryChange(newGeometry, oldGeometry);
731 d->updateLayout();
732}
733
734void QQuickDialogButtonBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
735{
736 Q_D(QQuickDialogButtonBox);
737 QQuickContainer::contentItemChange(newItem, oldItem);
738 if (oldItem)
739 QQuickItemPrivate::get(item: oldItem)->removeItemChangeListener(d, types: QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight);
740 if (newItem)
741 QQuickItemPrivate::get(item: newItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight);
742}
743
744bool QQuickDialogButtonBox::isContent(QQuickItem *item) const
745{
746 return qobject_cast<QQuickAbstractButton *>(object: item);
747}
748
749void QQuickDialogButtonBox::itemAdded(int index, QQuickItem *item)
750{
751 Q_D(QQuickDialogButtonBox);
752 Q_UNUSED(index);
753 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: item))
754 QObjectPrivate::connect(sender: button, signal: &QQuickAbstractButton::clicked, receiverPrivate: d, slot: &QQuickDialogButtonBoxPrivate::handleClick);
755 if (QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: item, create: false)))
756 QQuickDialogButtonBoxAttachedPrivate::get(q: attached)->setButtonBox(this);
757 d->updateImplicitContentSize();
758 if (isComponentComplete())
759 polish();
760}
761
762void QQuickDialogButtonBox::itemRemoved(int index, QQuickItem *item)
763{
764 Q_D(QQuickDialogButtonBox);
765 Q_UNUSED(index);
766 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: item))
767 QObjectPrivate::disconnect(sender: button, signal: &QQuickAbstractButton::clicked, receiverPrivate: d, slot: &QQuickDialogButtonBoxPrivate::handleClick);
768 if (QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: item, create: false)))
769 QQuickDialogButtonBoxAttachedPrivate::get(q: attached)->setButtonBox(nullptr);
770 d->updateImplicitContentSize();
771 if (isComponentComplete())
772 polish();
773}
774
775#if QT_CONFIG(accessibility)
776QAccessible::Role QQuickDialogButtonBox::accessibleRole() const
777{
778 return QAccessible::PageTabList;
779}
780#endif
781
782void QQuickDialogButtonBoxAttachedPrivate::setButtonBox(QQuickDialogButtonBox *box)
783{
784 Q_Q(QQuickDialogButtonBoxAttached);
785 if (buttonBox == box)
786 return;
787
788 buttonBox = box;
789 emit q->buttonBoxChanged();
790}
791
792QQuickDialogButtonBoxAttached::QQuickDialogButtonBoxAttached(QObject *parent)
793 : QObject(*(new QQuickDialogButtonBoxAttachedPrivate), parent)
794{
795 Q_D(QQuickDialogButtonBoxAttached);
796 QQuickItem *parentItem = qobject_cast<QQuickItem *>(o: parent);
797 while (parentItem && !d->buttonBox) {
798 d->buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: parentItem);
799 parentItem = parentItem->parentItem();
800 }
801}
802
803/*!
804 \qmlattachedproperty DialogButtonBox QtQuick.Controls::DialogButtonBox::buttonBox
805 \readonly
806
807 This attached property holds the button box that manages this button, or
808 \c null if the button is not in a button box.
809*/
810QQuickDialogButtonBox *QQuickDialogButtonBoxAttached::buttonBox() const
811{
812 Q_D(const QQuickDialogButtonBoxAttached);
813 return d->buttonBox;
814}
815
816/*!
817 \qmlattachedproperty enumeration QtQuick.Controls::DialogButtonBox::buttonRole
818
819 This attached property holds the role of each button in a button box.
820
821 \snippet qtquickcontrols-dialogbuttonbox-attached.qml 1
822
823 Available values:
824 \value DialogButtonBox.InvalidRole The button is invalid.
825 \value DialogButtonBox.AcceptRole Clicking the button causes the dialog to be accepted (e.g. \uicontrol OK).
826 \value DialogButtonBox.RejectRole Clicking the button causes the dialog to be rejected (e.g. \uicontrol Cancel).
827 \value DialogButtonBox.DestructiveRole Clicking the button causes a destructive change (e.g. for discarding changes) and closes the dialog.
828 \value DialogButtonBox.ActionRole Clicking the button causes changes to the elements within the dialog.
829 \value DialogButtonBox.HelpRole The button can be clicked to request help.
830 \value DialogButtonBox.YesRole The button is a "Yes"-like button.
831 \value DialogButtonBox.NoRole The button is a "No"-like button.
832 \value DialogButtonBox.ResetRole The button resets the dialog's fields to default values.
833 \value DialogButtonBox.ApplyRole The button applies current changes.
834*/
835QPlatformDialogHelper::ButtonRole QQuickDialogButtonBoxAttached::buttonRole() const
836{
837 Q_D(const QQuickDialogButtonBoxAttached);
838 return d->buttonRole;
839}
840
841void QQuickDialogButtonBoxAttached::setButtonRole(QPlatformDialogHelper::ButtonRole role)
842{
843 Q_D(QQuickDialogButtonBoxAttached);
844 if (d->buttonRole == role)
845 return;
846
847 d->buttonRole = role;
848 emit buttonRoleChanged();
849}
850
851QT_END_NAMESPACE
852
853#include "moc_qquickdialogbuttonbox_p.cpp"
854

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quicktemplates/qquickdialogbuttonbox.cpp