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