Warning, /plasma/plasma-systemmonitor/src/page/EditablePage.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 org.kde.kirigami 2.12 as Kirigami
0012 
0013 import org.kde.ksysguard.page 1.0
0014 
0015 Kirigami.ScrollablePage {
0016     id: page
0017 
0018     property PageDataObject pageData
0019     property bool edit: false
0020 
0021     title: pageData.title
0022 
0023     leftPadding: pageData.margin * Kirigami.Units.largeSpacing
0024     rightPadding: pageData.margin * Kirigami.Units.largeSpacing
0025     topPadding: pageData.margin * Kirigami.Units.largeSpacing
0026     bottomPadding: pageData.margin * Kirigami.Units.largeSpacing
0027 
0028     Kirigami.ColumnView.fillWidth: true
0029     Kirigami.ColumnView.reservedSpace: edit ? applicationWindow().pageStack.columnView.columnWidth : 0
0030 
0031     Binding {
0032         target: globalToolBarItem
0033         property: "enabled"
0034         value: contentLoader.status != Loader.Loading
0035     }
0036 
0037     readonly property real heightForContent: (parent ? parent.height : 0) - topPadding - bottomPadding - (globalToolBarItem ? globalToolBarItem.height : 0)
0038 
0039     readonly property var actionsFace: contentLoader.item && contentLoader.item.actionsFace ? contentLoader.item.actionsFace : null
0040     onActionsFaceChanged: Qt.callLater(updateActions)
0041     Connections {
0042         target: page.actionsFace
0043         function onPrimaryActionsChanged() { Qt.callLater(page.updateActions) }
0044         function onSecondaryActionsChanged() { Qt.callLater(page.updateActions) }
0045     }
0046 
0047     function updateActions() {
0048         actions.main = null
0049         actions.left = null
0050         actions.right = null
0051 
0052         if (!actionsFace || page.edit) {
0053             actions.contextualActions = defaultActions
0054             return
0055         }
0056 
0057         let primary = page.actionsFace.primaryActions
0058         let secondary = page.actionsFace.secondaryActions
0059 
0060         if (primary.length == 0 && secondary.length == 0) {
0061             if (actions.contextualActions == defaultActions) {
0062                 return;
0063             }
0064 
0065             actions.contextualActions = defaultActions
0066             return
0067         }
0068 
0069         if (primary.length >= 1) {
0070             actions.main = primary[0]
0071         }
0072 
0073         if (primary.length >= 2) {
0074             actions.left = primary[1]
0075         }
0076 
0077         if (primary.length >= 3) {
0078             actions.right = primary[2]
0079         }
0080 
0081         let contextual = []
0082 
0083         if (primary.length >= 4) {
0084             contextual = Array.prototype.map.call(primary, i => i).slice(4)
0085         }
0086 
0087         if (secondary.length > 0) {
0088             contextual = contextual.concat(Array.prototype.map.call(secondary, i => i))
0089         }
0090 
0091         actions.contextualActions = contextual.concat(defaultActions)
0092     }
0093 
0094     // Scroll the contents of the page based on the position of a rect (in scene
0095     // coordinates). If any part of the rect is above the visible area, contents
0096     // will be scrolled down. If any part of the rect is below the visible area,
0097     // contents will be scrolled up instead. This mimics dragging behaviour of
0098     // file managers and similar things.
0099     function scrollContents(rect) {
0100         let visibleRect = page.flickable.mapToItem(null, Qt.rect(page.flickable.x, page.flickable.y, page.flickable.width, page.flickable.height - page.bottomPadding))
0101 
0102         let minY = rect.y
0103         let maxY = rect.y + rect.height
0104 
0105         let visibleTop = visibleRect.y
0106         let visibleBottom = visibleRect.y + visibleRect.height
0107 
0108         if (minY >= visibleTop && maxY <= visibleBottom) {
0109             return
0110         }
0111 
0112         if (maxY > visibleBottom && !page.flickable.atYEnd) {
0113             page.flickable.contentY += (maxY - visibleBottom)
0114         } else if (minY < visibleTop && !page.flickable.atYBeginning) {
0115             page.flickable.contentY += (minY - visibleTop)
0116         }
0117         page.flickable.returnToBounds()
0118     }
0119 
0120     Kirigami.Action {
0121         id: editAction
0122 
0123         text: i18nc("@action", "Edit Page")
0124         icon.name: "document-edit"
0125         visible: !page.edit
0126 
0127         displayHint: page.actionsFace ? Kirigami.Action.DisplayHint.AlwaysHide : Kirigami.Action.DisplayHint.NoPreference
0128 
0129         onTriggered: page.edit = !page.edit
0130     }
0131     Kirigami.Action {
0132         id: saveAction
0133         text: i18nc("@action", "Save Changes")
0134         icon.name: "document-save"
0135         visible: page.edit
0136         onTriggered: {
0137             page.edit = false
0138             page.pageData.savePage()
0139         }
0140     }
0141     Kirigami.Action {
0142         id: discardAction
0143         text: i18nc("@action", "Discard Changes")
0144         icon.name: "edit-delete-remove"
0145         visible: page.edit
0146         onTriggered: {
0147             page.edit = false
0148             page.pageData.resetPage()
0149         }
0150     }
0151     Kirigami.Action {
0152         id: configureAction
0153 
0154         text: i18nc("@action", "Configure Pageā€¦")
0155         icon.name: "configure"
0156         visible: page.edit
0157 
0158         onTriggered: pageDialog.open()
0159     }
0160     Kirigami.Action {
0161         id: addRowAction
0162 
0163         text: i18nc("@action", "Add Row")
0164         icon.name: "edit-table-insert-row-under"
0165         visible: page.edit
0166 
0167         onTriggered: contentLoader.item.addRow(-1)
0168     }
0169 
0170     Kirigami.Action {
0171         id: addTitleAction
0172         text: i18nc("@action", "Add Title")
0173         icon.name: "insert-text-frame"
0174         visible: page.edit
0175 
0176         onTriggered: contentLoader.item.addTitle(-1)
0177     }
0178 
0179     readonly property var defaultActions: [editAction, saveAction, discardAction, addRowAction, addTitleAction, configureAction]
0180 
0181     Component {
0182         id: pageEditor
0183 
0184         PageEditor {
0185             parentPage: page
0186             pageData: page.pageData
0187         }
0188     }
0189 
0190     Component {
0191         id: pageContents
0192 
0193         PageContents {
0194             pageData: page.pageData
0195         }
0196     }
0197 
0198     data: [
0199         DialogLoader {
0200             id: pageDialog
0201             sourceComponent: PageDialog {
0202                 title: i18nc("@title:window %1 is page title", "Configure Page \"%1\"", page.pageData.title)
0203 
0204                 acceptText: i18nc("@action:button", "Save")
0205                 acceptIcon: "document-save"
0206 
0207                 onAboutToShow: {
0208                     name = page.pageData.title
0209                     iconName = page.pageData.icon
0210                     margin = page.pageData.margin
0211                     pageData = page.pageData
0212                     actionsFace = page.pageData.actionsFace ? page.pageData.actionsFace : ""
0213                     loadType = page.pageData.loadType ? page.pageData.loadType : "ondemand"
0214                 }
0215 
0216                 onAccepted: {
0217                     pageData.title = name
0218                     pageData.icon = iconName
0219                     pageData.margin = margin
0220                     pageData.actionsFace = actionsFace
0221                     pageData.loadType = loadType
0222                 }
0223             }
0224         },
0225         DialogLoader {
0226             id: missingSensorsDialog
0227 
0228             sourceComponent: MissingSensorsDialog {
0229                 missingSensors: contentLoader.item ? contentLoader.item.missingSensors : []
0230 
0231                 onSensorReplacementChanged: {
0232                     contentLoader.item.replaceSensors(sensorReplacement)
0233                 }
0234             }
0235         }
0236     ]
0237 
0238     Rectangle {
0239         id: loadOverlay
0240 
0241         parent: page.overlay
0242         anchors.fill: parent
0243         anchors.margins: -pageData.margin * Kirigami.Units.largeSpacing
0244         color: Kirigami.Theme.backgroundColor
0245 
0246         opacity: 1
0247         visible: opacity > 0
0248         Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.shortDuration } }
0249 
0250         BusyIndicator {
0251             anchors.centerIn: parent
0252             running: loadOverlay.visible
0253         }
0254     }
0255 
0256     Loader {
0257         id: contentLoader
0258 
0259         width: page.width
0260         height: item ? Math.max(item.Layout.minimumHeight, page.heightForContent) : page.heightForContent
0261 
0262         property real availableHeight: page.heightForContent
0263         property real rowSpacing: page.pageData.margin * Kirigami.Units.smallSpacing
0264 
0265         sourceComponent: page.edit ? pageEditor : pageContents
0266         asynchronous: true
0267 
0268         onStatusChanged: {
0269             if (status == Loader.Loading) {
0270                 loadOverlay.opacity = 1
0271                 if (!edit && applicationWindow().pageStack.columnView.containsItem(page)) {
0272                     // Pop any pages that might have been opened during editing
0273                     applicationWindow().pageStack.pop(page)
0274                 }
0275             } else {
0276                 Qt.callLater(updateActions)
0277                 page.flickable.returnToBounds()
0278                 loadOverlay.opacity = 0
0279             }
0280         }
0281 
0282         Connections {
0283             target: contentLoader.item
0284 
0285             function onShowMissingSensors() {
0286                 missingSensorsDialog.open()
0287             }
0288         }
0289     }
0290 }