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