Warning, /plasma/plasma-mobile/lookandfeel/contents/systemdialog/SystemDialog.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  *  SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Controls 2.15
0009 import QtQuick.Layouts 1.15
0010 import QtQuick.Window 2.15
0011 import Qt5Compat.GraphicalEffects
0012 import org.kde.kirigami 2.18 as Kirigami
0013 import "private" as Private
0014 
0015 Item {
0016     id: root
0017     
0018     default property Item mainItem
0019     
0020     /**
0021      * Title of the dialog.
0022      */
0023     property string mainText: ""
0024     
0025     /**
0026      * Subtitle of the dialog.
0027      */
0028     property string subtitle: ""
0029     
0030     /**
0031      * This property holds the default padding of the content.
0032      */
0033     property real padding: Kirigami.Units.smallSpacing
0034     
0035     /**
0036      * This property holds the left padding of the content. If not specified, it uses `padding`.
0037      */
0038     property real leftPadding: padding
0039     
0040     /**
0041      * This property holds the right padding of the content. If not specified, it uses `padding`.
0042      */
0043     property real rightPadding: padding
0044     
0045     /**
0046      * This property holds the top padding of the content. If not specified, it uses `padding`.
0047      */
0048     property real topPadding: padding
0049     
0050     /**
0051      * This property holds the bottom padding of the content. If not specified, it uses `padding`.
0052      */
0053     property real bottomPadding: padding
0054     property alias standardButtons: footerButtonBox.standardButtons
0055 
0056     readonly property int flags: Qt.FramelessWindowHint | Qt.Dialog
0057     readonly property real dialogCornerRadius: Kirigami.Units.smallSpacing * 2
0058     property list<Kirigami.Action> actions
0059     property string iconName
0060 
0061     implicitWidth: loader.implicitWidth
0062     implicitHeight: loader.implicitHeight
0063 
0064     readonly property real minimumHeight: implicitWidth
0065     readonly property real minimumWidth: implicitHeight
0066 
0067     required property Kirigami.AbstractApplicationWindow window
0068 
0069     function present() {
0070         root.window.showFullScreen()
0071     }
0072 
0073     onWindowChanged: {
0074         window.color = Qt.binding(() => {
0075             return Qt.rgba(0, 0, 0, 0.5)
0076         })
0077     }
0078 
0079     // load in async to speed up load times (especially on embedded devices)
0080     Loader {
0081         id: loader
0082         anchors.centerIn: parent
0083         asynchronous: true
0084 
0085         sourceComponent: Item {
0086             // margins for shadow
0087             implicitWidth: Math.min(Screen.width, control.implicitWidth + 2 * Kirigami.Units.gridUnit)
0088             implicitHeight: Math.min(Screen.height, control.implicitHeight + 2 * Kirigami.Units.gridUnit)
0089 
0090             // shadow
0091             RectangularGlow {
0092                 id: glow
0093                 anchors.topMargin: 1
0094                 anchors.fill: control
0095                 cached: true
0096                 glowRadius: 2
0097                 cornerRadius: Kirigami.Units.gridUnit
0098                 spread: 0.1
0099                 color: Qt.rgba(0, 0, 0, 0.4)
0100             }
0101 
0102             // actual window
0103             Control {
0104                 id: control
0105                 anchors.fill: parent
0106                 anchors.margins: glow.cornerRadius
0107                 topPadding: 0
0108                 bottomPadding: 0
0109                 rightPadding: 0
0110                 leftPadding: 0
0111 
0112                 background: Item {
0113                     Rectangle { // border
0114                         anchors.fill: parent
0115                         anchors.margins: -1
0116                         radius: dialogCornerRadius + 1
0117                         color: Qt.darker(Kirigami.Theme.backgroundColor, 1.5)
0118                     }
0119                     Rectangle { // background colour
0120                         anchors.fill: parent
0121                         radius: dialogCornerRadius
0122                         color: Kirigami.Theme.backgroundColor
0123                     }
0124                 }
0125 
0126                 contentItem: column
0127             }
0128         }
0129     }
0130     
0131     readonly property var contents: ColumnLayout {
0132         id: column
0133         spacing: 0
0134 
0135         // header
0136         Control {
0137             id: headerControl
0138 
0139             Layout.fillWidth: true
0140             Layout.maximumWidth: root.window.maximumWidth
0141 
0142             topPadding: 0
0143             leftPadding: 0
0144             rightPadding: 0
0145             bottomPadding: 0
0146 
0147             background: Item {}
0148 
0149             contentItem: RowLayout {
0150                 Kirigami.Heading {
0151                     Layout.fillWidth: true
0152                     Layout.topMargin: Kirigami.Units.largeSpacing
0153                     Layout.bottomMargin: Kirigami.Units.largeSpacing
0154                     Layout.leftMargin: Kirigami.Units.largeSpacing
0155                     Layout.rightMargin: Kirigami.Units.largeSpacing
0156                     Layout.alignment: Qt.AlignVCenter
0157                     level: 2
0158                     text: root.mainText
0159                     wrapMode: Text.Wrap
0160                     elide: Text.ElideRight
0161                     horizontalAlignment: Text.AlignHCenter
0162                 }
0163             }
0164         }
0165 
0166         // content
0167         Control {
0168             id: content
0169 
0170             Layout.fillHeight: true
0171             Layout.fillWidth: true
0172             Layout.maximumWidth: root.window.maximumWidth
0173 
0174             leftPadding: 0
0175             rightPadding: 0
0176             topPadding: 0
0177             bottomPadding: 0
0178 
0179             background: Item {}
0180             contentItem: ColumnLayout {
0181                 spacing: 0
0182                 clip: true
0183 
0184                 Label {
0185                     id: subtitleLabel
0186                     Layout.fillWidth: true
0187                     Layout.topMargin: Kirigami.Units.largeSpacing
0188                     Layout.bottomMargin: Kirigami.Units.largeSpacing
0189                     Layout.leftMargin: Kirigami.Units.gridUnit * 3
0190                     Layout.rightMargin: Kirigami.Units.gridUnit * 3
0191                     visible: root.subtitle !== ""
0192                     horizontalAlignment: Text.AlignHCenter
0193                     text: root.subtitle
0194                     wrapMode: Label.Wrap
0195                 }
0196 
0197                 // separator when scrolling
0198                 Kirigami.Separator {
0199                     Layout.fillWidth: true
0200                     opacity: root.mainItem && contentControl.flickableItem && contentControl.flickableItem.contentY !== 0 ? 1 : 0 // always maintain same height (as opposed to visible)
0201                 }
0202 
0203                 // mainItem is in scrollview, in case of overflow
0204                 Private.ScrollView {
0205                     id: contentControl
0206                     clip: true
0207 
0208                     // we cannot have contentItem inside a sub control (allowing for content padding within the scroll area),
0209                     // because if the contentItem is a Flickable (ex. ListView), the ScrollView needs it to be top level in order
0210                     // to decorate it
0211                     contentItem: root.mainItem
0212                     canFlickWithMouse: true
0213 
0214                     // ensure window colour scheme, and background color
0215                     Kirigami.Theme.inherit: false
0216                     Kirigami.Theme.colorSet: Kirigami.Theme.Window
0217 
0218                     // needs to explicitly be set for each side to work
0219                     leftPadding: root.leftPadding; topPadding: root.topPadding
0220                     rightPadding: root.rightPadding; bottomPadding: root.bottomPadding
0221 
0222                     // height of everything else in the dialog other than the content
0223                     property real otherHeights: headerControl.height + subtitleLabel.height + footerButtonBox.height + root.topPadding + root.bottomPadding;
0224 
0225                     property real calculatedMaximumWidth: root.window.maximumWidth > root.absoluteMaximumWidth ? root.absoluteMaximumWidth : root.window.maximumWidth
0226                     property real calculatedMaximumHeight: root.window.maximumHeight > root.absoluteMaximumHeight ? root.absoluteMaximumHeight : root.window.maximumHeight
0227                     property real calculatedImplicitWidth: root.mainItem ? (root.mainItem.implicitWidth ? root.mainItem.implicitWidth : root.mainItem.width) + root.leftPadding + root.rightPadding : 0
0228                     property real calculatedImplicitHeight: root.mainItem ? (root.mainItem.implicitHeight ? root.mainItem.implicitHeight : root.mainItem.height) + root.topPadding + root.bottomPadding : 0
0229 
0230                     // don't enforce preferred width and height if not set
0231                     Layout.preferredWidth: root.preferredWidth >= 0 ? root.preferredWidth : calculatedImplicitWidth + contentControl.rightSpacing
0232                     Layout.preferredHeight: root.preferredHeight >= 0 ? root.preferredHeight - otherHeights : calculatedImplicitHeight + contentControl.bottomSpacing
0233 
0234                     Layout.fillWidth: true
0235                     Layout.fillHeight: true
0236                     Layout.maximumWidth: calculatedMaximumWidth
0237                     Layout.maximumHeight: calculatedMaximumHeight >= otherHeights ? calculatedMaximumHeight - otherHeights : 0 // we enforce maximum height solely from the content
0238 
0239                     // give an implied width and height to the contentItem so that features like word wrapping/eliding work
0240                     // cannot placed directly in contentControl as a child, so we must use a property
0241                     property var widthHint: Binding {
0242                         target: root.mainItem
0243                         property: "width"
0244                         // we want to avoid horizontal scrolling, so we apply maximumWidth as a hint if necessary
0245                         property real preferredWidthHint: contentControl.width - root.leftPadding - root.rightPadding - contentControl.rightSpacing
0246                         property real maximumWidthHint: contentControl.calculatedMaximumWidth - root.leftPadding - root.rightPadding - contentControl.rightSpacing
0247                         value: maximumWidthHint < preferredWidthHint ? maximumWidthHint : preferredWidthHint
0248                     }
0249                     property var heightHint: Binding {
0250                         target: root.mainItem
0251                         property: "height"
0252                         // we are okay with overflow, if it exceeds maximumHeight we will allow scrolling
0253                         value: contentControl.Layout.preferredHeight - root.topPadding - root.bottomPadding - contentControl.bottomSpacing
0254                     }
0255 
0256                     // give explicit warnings since the maximumHeight is ignored when negative, so developers aren't confused
0257                     Component.onCompleted: {
0258                         if (contentControl.Layout.maximumHeight < 0 || contentControl.Layout.maximumHeight === Infinity) {
0259                             console.log("Dialog Warning: the calculated maximumHeight for the content is " + contentControl.Layout.maximumHeight + ", ignoring...");
0260                         }
0261                     }
0262                 }
0263             }
0264         }
0265         Control {
0266             Layout.fillWidth: true
0267 
0268             leftPadding: 0
0269             rightPadding: 0
0270             topPadding: 0
0271             bottomPadding: 0
0272             contentItem: footerButtonBox
0273         }
0274     }
0275 
0276     readonly property DialogButtonBox dialogButtonBox: DialogButtonBox {
0277         //We want to report the same width on all so the button area is split equally
0278         id: footerButtonBox
0279         Layout.fillWidth: true
0280         onAccepted: root.window.accept()
0281         onRejected: root.window.reject()
0282         implicitHeight: contentItem.implicitHeight
0283         alignment: undefined
0284 
0285         readonly property real sameWidth: 50
0286         delegate: Private.MobileSystemDialogButton {
0287             Layout.fillWidth: true
0288             Layout.preferredWidth: footerButtonBox.sameWidth
0289 
0290             readonly property point globalPos: root.window.visible ? mapToItem(footerButtonBox, Qt.point(x, y)) : Qt.point(0, 0)
0291             verticalSeparator: globalPos.x > 0 && root.window.layout === Qt.Horizontal
0292             horizontalSeparator: true
0293             corners.bottomLeftRadius: verticalSeparator ? 0 : root.dialogCornerRadius
0294             corners.bottomRightRadius: globalPos.x >= footerButtonBox.width ? root.dialogCornerRadius : 0
0295         }
0296 
0297         leftPadding: 0
0298         rightPadding: 0
0299         topPadding: 0
0300         bottomPadding: 0
0301 
0302         contentItem: GridLayout {
0303             Layout.fillWidth: true
0304 
0305             rowSpacing: 0
0306             columnSpacing: 0
0307             rows: root.window.layout === Qt.Vertical ? Number.MAX_VALUE : 1
0308             columns: root.window.layout !== Qt.Vertical ? Number.MAX_VALUE : 1
0309 
0310             Repeater {
0311                 model: root.actions
0312                 delegate: Private.MobileSystemDialogButton {
0313                     Layout.fillWidth: true
0314                     Layout.preferredWidth: footerButtonBox.sameWidth
0315                     readonly property point globalPos: root.window.visible ? mapToItem(footerButtonBox, Qt.point(x, y)) : Qt.point(0, 0)
0316                     verticalSeparator: globalPos.x > 0 && root.window.layout === Qt.Horizontal
0317                     horizontalSeparator: true
0318                     corners.bottomLeftRadius: model.index === root.actions.length - 1 ? root.dialogCornerRadius : 0
0319                     corners.bottomRightRadius: model.index === root.actions.length - 1 && footerButtonBox.standardButtons === 0 ? root.dialogCornerRadius : 0
0320                     action: modelData
0321                 }
0322             }
0323         }
0324     }
0325 }