Warning, /plasma/plasma-systemmonitor/src/page/PageContents.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 import QtQuick 2.12
0008 import QtQuick.Controls 2.12
0009 import QtQuick.Layouts 1.12
0010 
0011 import QtGraphicalEffects 1.0
0012 
0013 import org.kde.kirigami 2.12 as Kirigami
0014 import org.kde.ksysguard.faces 1.0 as Faces
0015 import org.kde.ksysguard.page 1.0
0016 
0017 ColumnLayout {
0018     id: root
0019 
0020     property PageDataObject pageData
0021     property var actionsFace
0022     property var missingSensors: []
0023 
0024     property var faceMapping: new Object()
0025 
0026     signal showMissingSensors()
0027 
0028     function replaceSensors(replacement) {
0029         if (!replacement) {
0030             return
0031         }
0032 
0033         missingSensors = []
0034 
0035         let modifiedControllers = []
0036 
0037         for (let entry of replacement) {
0038             let controller = faceMapping[entry.face].controller
0039             controller.replaceSensors(entry.sensor,  entry.replacement)
0040             modifiedControllers.push(controller)
0041         }
0042 
0043         for (let c of modifiedControllers) {
0044             c.reloadConfig()
0045         }
0046     }
0047 
0048     spacing: rowSpacing // From parent Loader
0049 
0050     readonly property real balancedRowHeight: {
0051         let reservedSpace = 0;
0052         let minimumSpace = 0;
0053         let balancedCount = 0;
0054         let maximumCount = 0;
0055 
0056         for (let i in children) {
0057             let child = children[i]
0058             if (!child.hasOwnProperty("heightMode") || !child.hasOwnProperty("minimumContentHeight")) {
0059                 continue
0060             }
0061 
0062             switch(child.heightMode) {
0063                 case "minimum":
0064                     reservedSpace += child.minimumContentHeight
0065                     break;
0066                 case "balanced":
0067                     minimumSpace = Math.max(child.minimumContentHeight, minimumSpace)
0068                     balancedCount += 1
0069                     break;
0070                 case "maximum":
0071                     maximumCount += 1
0072                     break;
0073             }
0074         }
0075 
0076         // If there's nothing to balance, we can ignore the rest.
0077         if (balancedCount == 0) {
0078             return 0
0079         }
0080 
0081         // If there's any rows that are set to "maximum", use the largest
0082         // minimum size for balanced rows.
0083         if (maximumCount > 0) {
0084             return minimumSpace
0085         }
0086 
0087         // Note that "availableHeight" here comes from the parent loader and
0088         // represents the available size of the content area of the page.
0089         let balancedHeight = (availableHeight - reservedSpace - root.spacing * (children.length - 1)) / balancedCount
0090         return Math.max(balancedHeight, minimumSpace)
0091     }
0092 
0093     Kirigami.InlineMessage {
0094         id: missingMessage
0095 
0096         Layout.fillWidth: true
0097 
0098         visible: root.missingSensors.length > 0
0099         type: Kirigami.MessageType.Error
0100         text: i18n("This page is missing some sensors and will not display correctly.");
0101 
0102         actions: Kirigami.Action {
0103             icon.name: "document-edit"
0104             text: i18nc("@action:button", "Fix…")
0105             onTriggered: root.showMissingSensors()
0106         }
0107     }
0108 
0109     Repeater {
0110         model: PageDataModel { data: root.pageData }
0111 
0112         Item {
0113             Layout.fillWidth: true
0114             Layout.fillHeight: heightMode != "minimum"
0115             Layout.preferredHeight: 0
0116             Layout.minimumHeight: {
0117                 if (heightMode == "minimum") {
0118                     return minimumContentHeight
0119                 }
0120 
0121                 if (heightMode == "balanced") {
0122                     return root.balancedRowHeight
0123                 }
0124 
0125                 return -1
0126             }
0127             Layout.maximumHeight: {
0128                 if (heightMode == "minimum") {
0129                     return minimumContentHeight
0130                 }
0131 
0132                 if (heightMode == "balanced") {
0133                     return root.balancedRowHeight
0134                 }
0135 
0136                 return -1
0137             }
0138 
0139             readonly property real minimumContentHeight: {
0140                 if (model.data.isTitle) {
0141                     return title.height
0142                 }
0143 
0144                 return rowContents.Layout.minimumHeight
0145             }
0146 
0147             readonly property string heightMode: {
0148                 if (model.data.heightMode) {
0149                     return model.data.heightMode
0150                 }
0151 
0152                 if (model.data.isTitle) {
0153                     return "minimum"
0154                 }
0155 
0156                 return "balanced"
0157             }
0158 
0159             Kirigami.Heading {
0160                 id: title
0161                 anchors.left: parent.left
0162                 level: 2
0163                 text: model.data.title ? model.data.title : ""
0164                 visible: model.data.isTitle
0165             }
0166 
0167             GridLayout {
0168                 id: rowContents
0169 
0170                 anchors.fill: parent
0171                 visible: !model.data.isTitle
0172 
0173                 columnSpacing: Kirigami.Units.largeSpacing
0174                 rowSpacing: Kirigami.Units.largeSpacing
0175 
0176                 Repeater {
0177                     model: PageDataModel { data: model.data }
0178 
0179                     Item {
0180                         Layout.fillWidth: true
0181                         Layout.fillHeight: true
0182                         Layout.minimumHeight: columnContents.Layout.minimumHeight + columnContents.anchors.topMargin + columnContents.anchors.bottomMargin
0183 
0184                         Kirigami.AbstractCard {
0185                             anchors.fill: parent
0186                             visible: model.data.showBackground
0187                         }
0188 
0189                         RowLayout {
0190                             id: columnContents
0191 
0192                             anchors.fill: parent
0193                             anchors.margins: model.data.noMargins ? 0 : Kirigami.Units.largeSpacing
0194 
0195                             spacing: Kirigami.Units.largeSpacing
0196 
0197                             layer.enabled: model.data.showBackground && model.data.noMargins
0198                             layer.effect: OpacityMask {
0199                                 maskSource: Kirigami.ShadowedRectangle {
0200                                     width: columnContents.width
0201                                     height: columnContents.height
0202                                     radius: Kirigami.Units.smallSpacing
0203                                 }
0204                             }
0205 
0206                             Repeater {
0207                                 model: PageDataModel { data: model.data }
0208 
0209                                 Loader {
0210                                     Layout.fillHeight: true
0211                                     Layout.fillWidth: model.data.isSeparator ? false : true
0212                                     Layout.preferredWidth: model.data.isSeparator ? 1 : 0
0213                                     Layout.minimumHeight: item ? item.Layout.minimumHeight : 0
0214 
0215                                     property var modelData: model
0216 
0217                                     sourceComponent: model.data.isSeparator ? separatorComponent : faceComponent
0218                                 }
0219                             }
0220                         }
0221                     }
0222                 }
0223             }
0224         }
0225     }
0226 
0227     Component {
0228         id: separatorComponent
0229 
0230         Kirigami.Separator { }
0231     }
0232 
0233     Component {
0234         id: faceComponent
0235 
0236         Control {
0237             leftPadding: 0
0238             rightPadding: 0
0239             topPadding: 0
0240             bottomPadding: 0
0241 
0242             Layout.minimumHeight: contentItem ? contentItem.Layout.minimumHeight : 0
0243 
0244             FaceLoader {
0245                 id: loader
0246                 dataObject: modelData.data
0247 
0248                 Component.onCompleted: {
0249                     root.faceMapping[modelData.data.face] = loader
0250                 }
0251             }
0252 
0253             Connections {
0254                 target: loader.controller
0255 
0256                 function onMissingSensorsChanged() {
0257                     for (let i of missingSensors) {
0258                         root.missingSensors.push({
0259                             "face": modelData.data.face,
0260                             "title": loader.controller.title,
0261                             "sensor": i
0262                         })
0263                     }
0264                     root.missingSensorsChanged()
0265                 }
0266 
0267                 Component.onCompleted: {
0268                     root.faceMapping[modelData.data.face] = loader
0269                 }
0270             }
0271 
0272             Component.onCompleted: {
0273                 loader.controller.fullRepresentation.formFactor = Faces.SensorFace.Constrained;
0274                 contentItem = loader.controller.fullRepresentation
0275 
0276                 if (root.pageData.actionsFace == modelData.data.face) {
0277                     root.actionsFace = loader.controller.fullRepresentation
0278                 }
0279             }
0280             TapHandler {
0281                 acceptedButtons: Qt.RightButton
0282                 enabled: WidgetExporter.plasmashellAvailable
0283                 onTapped: {
0284                     const point = parent.mapToItem(contextMenu.parent, eventPoint.position)
0285                     contextMenu.x = point.x
0286                     contextMenu.y = point.y
0287                     contextMenu.loader = loader
0288                     contextMenu.open()
0289                 }
0290             }
0291         }
0292     }
0293     Menu {
0294         id: contextMenu
0295         property QtObject loader
0296         MenuItem {
0297             icon.name: "document-export"
0298             text: i18nc("@action", "Add Chart as Desktop Widget")
0299             onTriggered: {
0300                 WidgetExporter.exportAsWidget(contextMenu.loader)
0301             }
0302         }
0303     }
0304 }