Warning, /frameworks/kirigami/src/controls/Dialog.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com> 0003 SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com> 0004 SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk> 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 import QtQuick 2.15 0009 import QtQml 2.15 0010 import QtQuick.Layouts 1.15 0011 import QtQuick.Templates 2.15 as T 0012 import QtQuick.Controls 2.15 as QQC2 0013 import org.kde.kirigami 2.12 as Kirigami 0014 import QtGraphicalEffects 1.12 as GE 0015 0016 /** 0017 * @brief Popup dialog that is used for short tasks and user interaction. 0018 * 0019 * Dialog consists of three components: 0020 * the <a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-dialog.html#header-prop">header</a>, 0021 * the <a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-popup.html#contentItem-prop">contentItem</a>, 0022 * and the <a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-dialog.html#footer-prop">footer</a>. 0023 * 0024 * By default, the header is a heading with text specified by the 0025 * <a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-dialog.html#title-prop">title</a> property. 0026 * 0027 * By default, the footer consists of a row of buttons specified by 0028 * the <a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-dialog.html#standardButtons-prop">standardButtons</a> 0029 * and ::customFooterActions properties. 0030 * 0031 * The `implicitHeight` and `implicitWidth` of the dialog contentItem is 0032 * the primary hint used for the dialog size. The dialog will be the 0033 * minimum size required for the header, footer and content unless 0034 * it is larger than `maximumHeight` and `maximumWidth`. Use 0035 * `preferredHeight` and `preferredWidth` in order to manually specify 0036 * a size for the dialog. 0037 * 0038 * If the content height exceeds the maximum height of the dialog, the 0039 * dialog's contents will become scrollable. 0040 * 0041 * If the contentItem is a QtQuick.ListView, 0042 * the dialog will take care of the necessary scrollbars and scrolling behaviour. 0043 * Do @b not attempt 0044 * to nest ListViews (it must be the top level item), as the scrolling 0045 * behaviour will not be handled. Use ListView's `header` and `footer` instead. 0046 * 0047 * Example for a selection dialog: 0048 * @code{.qml} 0049 * import QtQuick 2.15 0050 * import QtQuick.Layouts 1.15 0051 * import QtQuick.Controls 2.15 as Controls 0052 * import org.kde.kirigami 2.19 as Kirigami 0053 * 0054 * Kirigami.Dialog { 0055 * title: i18n("Dialog") 0056 * padding: 0 0057 * preferredWidth: Kirigami.Units.gridUnit * 16 0058 * 0059 * standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel 0060 * 0061 * onAccepted: console.log("OK button pressed") 0062 * onRejected: console.log("Rejected") 0063 * 0064 * ColumnLayout { 0065 * spacing: 0 0066 * Repeater { 0067 * model: 5 0068 * delegate: Controls.CheckDelegate { 0069 * topPadding: Kirigami.Units.smallSpacing * 2 0070 * bottomPadding: Kirigami.Units.smallSpacing * 2 0071 * Layout.fillWidth: true 0072 * text: modelData 0073 * } 0074 * } 0075 * } 0076 * } 0077 * @endcode 0078 * 0079 * Example with scrolling (ListView scrolling behaviour is handled by the Dialog): 0080 * @code{.qml} 0081 * Kirigami.Dialog { 0082 * id: scrollableDialog 0083 * title: i18n("Select Number") 0084 * 0085 * ListView { 0086 * id: listView 0087 * // hints for the dialog dimensions 0088 * implicitWidth: Kirigami.Units.gridUnit * 16 0089 * implicitHeight: Kirigami.Units.gridUnit * 16 0090 * 0091 * model: 100 0092 * delegate: Controls.RadioDelegate { 0093 * topPadding: Kirigami.Units.smallSpacing * 2 0094 * bottomPadding: Kirigami.Units.smallSpacing * 2 0095 * implicitWidth: listView.width 0096 * text: modelData 0097 * } 0098 * } 0099 * } 0100 * @endcode 0101 * 0102 * There are also sub-components of the Dialog that target specific usecases, 0103 * and can reduce boilerplate code if used: 0104 * 0105 * @see kirigami::PromptDialog 0106 * @see kirigami::MenuDialog 0107 * @see <a href="https://develop.kde.org/hig/components/navigation/dialog">Human Interface Guidelines on Dialogs</a> 0108 * @see <a href="https://develop.kde.org/hig/components/assistance/message">Human Interface Guidelines on Modal Message Dialogs</a> 0109 * @inherit QtQuick.QtObject 0110 */ 0111 T.Dialog { 0112 id: root 0113 0114 // TODO KF6: remove this property 0115 /** 0116 * @deprecated This property will be removed in the next major frameworks release (KF6) 0117 */ 0118 property Item mainItem: contentControl.contentChildren.length > 0 ? contentControl.contentChildren[0] : null 0119 0120 /** 0121 * @brief This property holds the dialog's contents; includes Items and QtObjects. 0122 * @property list<QtObject> dialogData 0123 */ 0124 default property alias dialogData: contentControl.contentData 0125 0126 /** 0127 * @brief This property holds the content items of the dialog. 0128 * 0129 * The initial height and width of the dialog is calculated from the 0130 * `implicitWidth` and `implicitHeight` of the content. 0131 * 0132 * @property list<Item> dialogChildren 0133 */ 0134 property alias dialogChildren: contentControl.contentChildren 0135 0136 /** 0137 * @brief This property sets the absolute maximum height the dialog can have. 0138 * 0139 * The height restriction is solely applied on the content, so if the 0140 * maximum height given is not larger than the height of the header and 0141 * footer, it will be ignored. 0142 * 0143 * This is the window height, subtracted by Kirigami.Units.largeSpacing on both the top 0144 * and bottom. 0145 * 0146 * @see maximumHeight 0147 */ 0148 readonly property real absoluteMaximumHeight: parent.height - Kirigami.Units.largeSpacing * 2 0149 0150 /** 0151 * @brief This property holds the absolute maximum width the dialog can have. 0152 * 0153 * By default, it is the window width, subtracted by Kirigami.Units.largeSpacing on both 0154 * the top and bottom. 0155 * 0156 * @see maximumWidth 0157 */ 0158 readonly property real absoluteMaximumWidth: parent.width - Kirigami.Units.largeSpacing * 2 0159 0160 /** 0161 * @brief This property holds the maximum height the dialog can have 0162 * (including the header and footer). 0163 * 0164 * The height restriction is solely enforced on the content, so if the 0165 * maximum height given is not larger than the height of the header and 0166 * footer, it will be ignored. 0167 * 0168 * By default, this is ::absoluteMaximumHeight. 0169 */ 0170 property real maximumHeight: absoluteMaximumHeight 0171 0172 /** 0173 * @brief This property holds the maximum width the dialog can have. 0174 * 0175 * By default, this is ::absoluteMaximumWidth. 0176 */ 0177 property real maximumWidth: absoluteMaximumWidth 0178 0179 /** 0180 * @brief This property holds the preferred height of the dialog. 0181 * 0182 * The content will receive a hint for how tall it should be to have 0183 * the dialog to be this height. 0184 * 0185 * If the content, header or footer require more space, then the height 0186 * of the dialog will expand to the necessary amount of space. 0187 */ 0188 property real preferredHeight: -1 0189 0190 /** 0191 * @brief This property holds the preferred width of the dialog. 0192 * 0193 * The content will receive a hint for how wide it should be to have 0194 * the dialog be this wide. 0195 * 0196 * If the content, header or footer require more space, then the width 0197 * of the dialog will expand to the necessary amount of space. 0198 */ 0199 property real preferredWidth: -1 0200 0201 0202 /** 0203 * @brief This property holds the component to the left of the footer buttons. 0204 */ 0205 property Component footerLeadingComponent 0206 0207 /** 0208 * @brief his property holds the component to the right of the footer buttons. 0209 */ 0210 property Component footerTrailingComponent 0211 0212 /** 0213 * @brief This property sets whether to show the close button in the header. 0214 */ 0215 property bool showCloseButton: true 0216 0217 /** 0218 * @brief This property sets whether the footer button style should be flat. 0219 */ 0220 property bool flatFooterButtons: false 0221 0222 /** 0223 * @brief This property holds the custom actions displayed in the footer. 0224 * 0225 * Example usage: 0226 * @code{.qml} 0227 * import QtQuick 2.15 0228 * import QtQuick.Controls 2.15 as Controls 0229 * import org.kde.kirigami 2.18 as Kirigami 0230 * 0231 * Kirigami.PromptDialog { 0232 * id: dialog 0233 * title: i18n("Confirm Playback") 0234 * subtitle: i18n("Are you sure you want to play this song? It's really loud!") 0235 * 0236 * standardButtons: Kirigami.Dialog.Cancel 0237 * customFooterActions: [ 0238 * Kirigami.Action { 0239 * text: i18n("Play") 0240 * iconName: "media-playback-start" 0241 * onTriggered: { 0242 * //... 0243 * dialog.close(); 0244 * } 0245 * } 0246 * ] 0247 * } 0248 * @endcode 0249 * @see kirigami::Action 0250 */ 0251 property list<Kirigami.Action> customFooterActions 0252 0253 // default standard button 0254 standardButtons: QQC2.Dialog.Close 0255 0256 function standardButton(button): T.AbstractButton { 0257 // in case a footer is redefined 0258 if (footer instanceof T.DialogButtonBox) { 0259 return footer.standardButton(button); 0260 } else if (footer === footerToolBar) { 0261 return dialogButtonBox.standardButton(button); 0262 } else { 0263 return null; 0264 } 0265 } 0266 0267 // calculate dimensions 0268 implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding // maximum width enforced from our content (one source of truth) to avoid binding loops 0269 implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding 0270 + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0) 0271 + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0); 0272 0273 // misc. dialog settings 0274 closePolicy: QQC2.Popup.CloseOnEscape | QQC2.Popup.CloseOnReleaseOutside 0275 modal: true 0276 clip: false 0277 padding: 0 0278 0279 // determine parent so that popup knows which window to popup in 0280 // we want to open the dialog in the center of the window, if possible 0281 Component.onCompleted: { 0282 if (typeof applicationWindow !== "undefined") { 0283 parent = applicationWindow().overlay; 0284 } 0285 } 0286 0287 // center dialog 0288 x: Math.round((parent.width - width) / 2) 0289 y: Math.round((parent.height - height) / 2) + Kirigami.Units.gridUnit * 2 * (1 - opacity) // move animation 0290 0291 // dialog enter and exit transitions 0292 enter: Transition { 0293 NumberAnimation { property: "opacity"; from: 0; to: 1; easing.type: Easing.InOutQuad; duration: Kirigami.Units.longDuration } 0294 } 0295 exit: Transition { 0296 NumberAnimation { property: "opacity"; from: 1; to: 0; easing.type: Easing.InOutQuad; duration: Kirigami.Units.longDuration } 0297 } 0298 0299 // black background, fades in and out 0300 QQC2.Overlay.modal: Rectangle { 0301 color: Qt.rgba(0, 0, 0, 0.3) 0302 0303 // the opacity of the item is changed internally by QQuickPopup on open/close 0304 Behavior on opacity { 0305 OpacityAnimator { 0306 duration: Kirigami.Units.longDuration 0307 easing.type: Easing.InOutQuad 0308 } 0309 } 0310 } 0311 0312 // dialog view background 0313 background: Item { 0314 GE.RectangularGlow { 0315 anchors.fill: rect 0316 anchors.topMargin: 1 0317 cornerRadius: rect.radius * 2 0318 glowRadius: 2 0319 spread: 0.2 0320 color: Qt.rgba(0, 0, 0, 0.3) 0321 } 0322 0323 Rectangle { 0324 id: rect 0325 anchors.fill: parent 0326 Kirigami.Theme.colorSet: Kirigami.Theme.View 0327 Kirigami.Theme.inherit: false 0328 color: Kirigami.Theme.backgroundColor 0329 radius: Kirigami.Units.smallSpacing 0330 } 0331 } 0332 0333 // dialog content 0334 contentItem: ColumnLayout { 0335 QQC2.ScrollView { 0336 id: contentControl 0337 0338 // ensure view colour scheme, and background color 0339 Kirigami.Theme.inherit: false 0340 Kirigami.Theme.colorSet: Kirigami.Theme.View 0341 0342 QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff 0343 0344 // height of everything else in the dialog other than the content 0345 property real otherHeights: root.header.height + root.footer.height + root.topPadding + root.bottomPadding; 0346 0347 property real calculatedMaximumWidth: Math.min(root.absoluteMaximumWidth, root.maximumWidth) - root.leftPadding - root.rightPadding 0348 property real calculatedMaximumHeight: Math.min(root.absoluteMaximumHeight, root.maximumHeight) - root.topPadding - root.bottomPadding 0349 property real calculatedImplicitWidth: (contentChildren.length === 1 && contentChildren[0].implicitWidth > 0 0350 ? contentChildren[0].implicitWidth 0351 : (contentItem.implicitWidth > 0 ? contentItem.implicitWidth : contentItem.width)) + leftPadding + rightPadding 0352 property real calculatedImplicitHeight: (contentChildren.length === 1 && contentChildren[0].implicitHeight > 0 0353 ? contentChildren[0].implicitHeight 0354 : (contentItem.implicitHeight > 0 ? contentItem.implicitHeight : contentItem.height)) + topPadding + bottomPadding 0355 0356 // how do we deal with the scrollbar width? 0357 // - case 1: the dialog itself has the preferredWidth set 0358 // -> we hint a width to the content so it shrinks to give space to the scrollbar 0359 // - case 2: preferredWidth not set, so we are using the content's implicit width 0360 // -> we expand the dialog's width to accommodate the scrollbar width (to respect the content's desired width) 0361 0362 // don't enforce preferred width and height if not set 0363 Layout.preferredWidth: (root.preferredWidth >= 0 ? root.preferredWidth : calculatedImplicitWidth) 0364 Layout.preferredHeight: root.preferredHeight >= 0 ? root.preferredHeight - otherHeights : calculatedImplicitHeight 0365 0366 Layout.fillWidth: true 0367 Layout.maximumWidth: calculatedMaximumWidth 0368 Layout.maximumHeight: calculatedMaximumHeight - otherHeights // we enforce maximum height solely from the content 0369 0370 // give an implied width and height to the contentItem so that features like word wrapping/eliding work 0371 // cannot placed directly in contentControl as a child, so we must use a property 0372 property var widthHint: Binding { 0373 target: contentControl.contentChildren[0] || null 0374 property: "width" 0375 0376 // we want to avoid horizontal scrolling, so we apply maximumWidth as a hint if necessary 0377 property real preferredWidthHint: contentControl.contentItem.width 0378 property real maximumWidthHint: contentControl.calculatedMaximumWidth - contentControl.leftPadding - contentControl.rightPadding 0379 0380 value: Math.min(maximumWidthHint, preferredWidthHint) 0381 0382 restoreMode: Binding.RestoreBinding 0383 } 0384 } 0385 } 0386 0387 header: T.Control { 0388 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, 0389 implicitContentWidth + leftPadding + rightPadding) 0390 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, 0391 implicitContentHeight + topPadding + bottomPadding) 0392 0393 padding: Kirigami.Units.largeSpacing 0394 bottomPadding: verticalPadding + headerSeparator.implicitHeight // add space for bottom separator 0395 0396 contentItem: RowLayout { 0397 Kirigami.Heading { 0398 id: heading 0399 Layout.fillWidth: true 0400 Layout.alignment: Qt.AlignVCenter 0401 level: 2 0402 text: root.title === "" ? " " : root.title // always have text to ensure header height 0403 elide: Text.ElideRight 0404 0405 // use tooltip for long text that is elided 0406 QQC2.ToolTip.visible: truncated && titleHoverHandler.hovered 0407 QQC2.ToolTip.text: root.title 0408 HoverHandler { id: titleHoverHandler } 0409 } 0410 Kirigami.Icon { 0411 id: closeIcon 0412 visible: root.showCloseButton 0413 0414 // We want to position the close button in the top-right 0415 // corner if the header is very tall, but we want to 0416 // vertically center it in a short header 0417 readonly property bool tallHeader: parent.height > (Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.largeSpacing + Kirigami.Units.largeSpacing) 0418 Layout.alignment: tallHeader ? Qt.AlignRight | Qt.AlignTop : Qt.AlignRight | Qt.AlignVCenter 0419 Layout.topMargin: tallHeader ? Kirigami.Units.largeSpacing : 0 0420 implicitHeight: Kirigami.Units.iconSizes.smallMedium 0421 implicitWidth: implicitHeight 0422 0423 source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic" 0424 active: closeMouseArea.containsMouse 0425 MouseArea { 0426 id: closeMouseArea 0427 hoverEnabled: Qt.styleHints.useHoverEffects 0428 anchors.fill: parent 0429 onClicked: mouse => root.reject() 0430 } 0431 } 0432 } 0433 0434 // header background 0435 background: Kirigami.ShadowedRectangle { 0436 corners.topLeftRadius: Kirigami.Units.smallSpacing 0437 corners.topRightRadius: Kirigami.Units.smallSpacing 0438 Kirigami.Theme.colorSet: Kirigami.Theme.Header 0439 Kirigami.Theme.inherit: false 0440 color: Kirigami.Theme.backgroundColor 0441 Kirigami.Separator { 0442 id: headerSeparator 0443 width: parent.width 0444 anchors.bottom: parent.bottom 0445 } 0446 } 0447 } 0448 0449 // use top level control rather than toolbar, since toolbar causes button rendering glitches 0450 footer: T.Control { 0451 id: footerToolBar 0452 0453 // if there is nothing in the footer, still maintain a height so that we can create a rounded bottom buffer for the dialog 0454 property bool bufferMode: contentItem.implicitHeight === 0 0455 implicitHeight: bufferMode ? Kirigami.Units.smallSpacing : contentItem.implicitHeight 0456 0457 leftPadding: 0; rightPadding: 0; bottomPadding: 0 0458 topPadding: bufferMode ? 0 : footerSeparator.implicitHeight // add space for the separator above the footer 0459 0460 contentItem: RowLayout { 0461 spacing: parent.spacing 0462 // Don't let user interact with footer during transitions 0463 enabled: root.opened 0464 0465 Loader { 0466 id: leadingLoader 0467 sourceComponent: root.footerLeadingComponent 0468 } 0469 0470 // footer buttons 0471 QQC2.DialogButtonBox { 0472 // we don't explicitly set padding, to let the style choose the padding 0473 id: dialogButtonBox 0474 standardButtons: root.standardButtons 0475 visible: count > 0 0476 0477 Layout.fillWidth: true 0478 Layout.alignment: dialogButtonBox.alignment 0479 0480 position: QQC2.DialogButtonBox.Footer 0481 0482 // ensure themes don't add a background, since it can lead to visual inconsistencies 0483 // with the rest of the dialog 0484 background: null 0485 0486 // we need to hook all of the buttonbox events to the dialog events 0487 onAccepted: root.accept() 0488 onRejected: root.reject() 0489 onApplied: root.applied() 0490 onDiscarded: root.discarded() 0491 onHelpRequested: root.helpRequested() 0492 onReset: root.reset() 0493 0494 // add custom footer buttons 0495 Repeater { 0496 model: root.customFooterActions 0497 // we have to use Button instead of ToolButton, because ToolButton has no visual distinction when disabled 0498 delegate: QQC2.Button { 0499 flat: flatFooterButtons 0500 action: modelData 0501 visible: modelData.visible 0502 } 0503 } 0504 } 0505 0506 Loader { 0507 id: trailingLoader 0508 sourceComponent: root.footerTrailingComponent 0509 } 0510 } 0511 0512 background: Kirigami.ShadowedRectangle { 0513 // curved footer bottom corners 0514 corners.bottomLeftRadius: Kirigami.Units.smallSpacing 0515 corners.bottomRightRadius: Kirigami.Units.smallSpacing 0516 0517 // we act as a content buffer if nothing is in the footer 0518 Kirigami.Theme.colorSet: footerToolBar.bufferMode ? Kirigami.Theme.View : Kirigami.Theme.Window 0519 Kirigami.Theme.inherit: false 0520 color: Kirigami.Theme.backgroundColor 0521 0522 // separator above footer 0523 Kirigami.Separator { 0524 id: footerSeparator 0525 visible: !footerToolBar.bufferMode 0526 width: parent.width 0527 anchors.top: parent.top 0528 } 0529 } 0530 } 0531 }