Warning, /plasma/flatpak-kcm/ui/main.qml is written in an unsupported language. File is not indexed.

0001 /**
0002  * SPDX-FileCopyrightText: 2022 Suhaas Joshi <joshiesuhaas0@gmail.com>
0003  * SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 pragma ComponentBehavior: Bound
0008 
0009 import QtQuick
0010 import QtQuick.Layouts
0011 import QtQuick.Controls as QQC2
0012 import org.kde.kirigami as Kirigami
0013 import org.kde.kcmutils as KCM
0014 import org.kde.kitemmodels as KItemModels
0015 import org.kde.plasma.kcm.flatpakpermissions
0016 
0017 KCM.ScrollViewKCM {
0018     id: root
0019     title: i18n("Flatpak Applications")
0020     Kirigami.ColumnView.fillWidth: false
0021     implicitWidth: Kirigami.Units.gridUnit * 40
0022     implicitHeight: Kirigami.Units.gridUnit * 20
0023     framedView: false
0024 
0025     function shouldChange(toAppAtFilteredRowIndex: int) {
0026         const fromAppAtSourceRowIndex = KCM.ConfigModule.currentIndex();
0027 
0028         const toAppAtSourceRowIndex = (toAppAtFilteredRowIndex === -1)
0029             ? toAppAtFilteredRowIndex
0030             : filteredRefsModel.mapToSource(filteredRefsModel.index(toAppAtFilteredRowIndex, 0)).row;
0031 
0032         if (toAppAtSourceRowIndex === KCM.ConfigModule.currentIndex()) {
0033             // Don't reload if it's current anyway.
0034             return;
0035         }
0036 
0037         if (fromAppAtSourceRowIndex !== -1 && KCM.ConfigModule.isSaveNeeded()) {
0038             const m = KCM.ConfigModule.refsModel;
0039             const fromAppAtSourceIndex = m.index(fromAppAtSourceRowIndex, 0);
0040             const applicationName = m.data(fromAppAtSourceIndex, FlatpakReferencesModel.Name);
0041             const applicationIcon = m.data(fromAppAtSourceIndex, FlatpakReferencesModel.Icon);
0042             const dialog = applyOrDiscardDialogComponent.createObject(this, {
0043                 applicationName,
0044                 applicationIcon,
0045                 fromAppAtSourceRowIndex,
0046                 toAppAtSourceRowIndex,
0047             });
0048             dialog.open();
0049         } else {
0050             changeApp(toAppAtSourceRowIndex)
0051         }
0052     }
0053 
0054     function changeApp(toAppAtSourceRowIndex) {
0055         let ref = null;
0056         if (toAppAtSourceRowIndex !== -1) {
0057             const sourceIndex = KCM.ConfigModule.refsModel.index(toAppAtSourceRowIndex, 0);
0058             ref = KCM.ConfigModule.refsModel.data(sourceIndex, FlatpakReferencesModel.Ref) as FlatpakReference;
0059             appsListView.setCurrentIndexLater(toAppAtSourceRowIndex);
0060         }
0061         KCM.ConfigModule.pop();
0062         KCM.ConfigModule.setIndex(toAppAtSourceRowIndex);
0063         KCM.ConfigModule.push("permissions.qml", { ref });
0064     }
0065 
0066     Component.onCompleted: {
0067         KCM.ConfigModule.columnWidth = Kirigami.Units.gridUnit * 15
0068         changeApp(KCM.ConfigModule.currentIndex());
0069     }
0070 
0071     KCM.ConfigModule.buttons: KCM.ConfigModule.Apply | KCM.ConfigModule.Default
0072 
0073     Component {
0074         id: applyOrDiscardDialogComponent
0075 
0076         Kirigami.PromptDialog {
0077             id: dialog
0078 
0079             required property string applicationName
0080             required property url applicationIcon
0081 
0082             // source model indices
0083             required property int fromAppAtSourceRowIndex
0084             required property int toAppAtSourceRowIndex
0085 
0086             readonly property bool narrow: (parent.width - leftMargin - rightMargin) < Kirigami.Units.gridUnit * 20
0087 
0088             parent: root.Kirigami.ColumnView.view
0089             title: i18n("Apply Permissions")
0090             subtitle: i18n("The permissions of application %1 have been changed. Do you want to apply these changes or discard them?", applicationName)
0091             standardButtons: QQC2.Dialog.Apply | QQC2.Dialog.Discard
0092 
0093             GridLayout {
0094                 columns: dialog.narrow ? 1 : 2
0095                 columnSpacing: Kirigami.Units.largeSpacing
0096                 rowSpacing: Kirigami.Units.largeSpacing
0097 
0098                 Kirigami.Icon {
0099                     source: dialog.applicationIcon.toString() !== "" ? dialog.applicationIcon : "application-vnd.flatpak.ref"
0100 
0101                     Layout.alignment: Qt.AlignCenter
0102                     Layout.preferredWidth: Kirigami.Units.iconSizes.large
0103                     Layout.preferredHeight: Kirigami.Units.iconSizes.large
0104                 }
0105                 Kirigami.SelectableLabel {
0106                     text: dialog.subtitle
0107                     wrapMode: Text.Wrap
0108 
0109                     Layout.fillWidth: true
0110                     Layout.fillHeight: true
0111                 }
0112             }
0113 
0114             QQC2.Overlay.modal: KcmPopupModal {}
0115 
0116             onOpened: {
0117                 const button = standardButton(QQC2.Dialog.Apply);
0118                 button.forceActiveFocus(Qt.TabFocusReason);
0119             }
0120 
0121             onApplied: {
0122                 root.KCM.ConfigModule.save()
0123                 root.changeApp(toAppAtSourceRowIndex)
0124                 dialog.close()
0125             }
0126 
0127             onDiscarded: {
0128                 root.KCM.ConfigModule.load()
0129                 root.changeApp(fromAppAtSourceRowIndex)
0130                 dialog.close()
0131             }
0132 
0133             onRejected: {
0134                 appsListView.currentIndex = root.KCM.ConfigModule.currentIndex()
0135             }
0136 
0137             onClosed: destroy()
0138         }
0139     }
0140 
0141     header: Kirigami.SearchField {
0142         id: filterField
0143         KeyNavigation.tab: appsListView
0144         KeyNavigation.down: appsListView
0145         autoAccept: false
0146         onAccepted: {
0147             if (text === "") {
0148                 // The signal also fires when user clicks the "Clear" action/icon/button.
0149                 // In this case don't treat it as a command to open first app.
0150                 return;
0151             }
0152             if (filteredRefsModel.count >= 0) {
0153                 appsListView.setCurrentIndexLater(0);
0154                 root.shouldChange(0);
0155             }
0156         }
0157     }
0158 
0159     view: ListView {
0160         id: appsListView
0161 
0162         function setCurrentIndexLater(sourceRowIndex) {
0163             // View has not updated yet, and alas -- we don't have suitable
0164             // hooks here. Note that ListView::onCountChanged WON'T WORK, as
0165             // unlike KSortFilterProxyModel it won't trigger when changing
0166             // from e.g. 2 rows [a, b] to another 2 rows [d, c].
0167 
0168             const sourceIndex = filteredRefsModel.sourceModel.index(sourceRowIndex, 0);
0169             const filteredIndex = filteredRefsModel.mapFromSource(sourceIndex);
0170             const filteredRowIndex = filteredIndex.valid ? filteredIndex.row : -1;
0171 
0172             delayedCurrentIndexSetter.index = filteredRowIndex;
0173             delayedCurrentIndexSetter.start();
0174         }
0175 
0176         // Using Timer object instead of Qt.callLater to get deduplication for free.
0177         Timer {
0178             id: delayedCurrentIndexSetter
0179 
0180             property int index: -1
0181 
0182             interval: 0
0183             running: false
0184 
0185             onTriggered: {
0186                 appsListView.currentIndex = index;
0187             }
0188         }
0189 
0190         model: KItemModels.KSortFilterProxyModel {
0191             id: filteredRefsModel
0192             sourceModel: root.KCM.ConfigModule.refsModel
0193             sortOrder: Qt.AscendingOrder
0194             sortCaseSensitivity: Qt.CaseInsensitive
0195             sortRole: FlatpakReferencesModel.Name
0196             filterRole: FlatpakReferencesModel.Name
0197             filterString: filterField.text
0198             filterCaseSensitivity: Qt.CaseInsensitive
0199             onCountChanged: {
0200                 if (count >= 0) {
0201                     const sourceRowIndex = root.KCM.ConfigModule.currentIndex();
0202                     appsListView.setCurrentIndexLater(sourceRowIndex);
0203                 }
0204             }
0205         }
0206         currentIndex: -1
0207         delegate: QQC2.ItemDelegate {
0208             required property int index
0209             required property var model
0210 
0211             width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
0212 
0213             highlighted: ListView.isCurrentItem
0214 
0215             text: model.name
0216             // Prefer source, fallback to name. This is unusual for QtQuick.Controls.
0217             icon.name: model.icon.toString() !== "" ? "" : "application-vnd.flatpak.ref"
0218             icon.source: model.icon.toString() === "" ? "" : model.icon
0219 
0220             onClicked: root.shouldChange(index)
0221         }
0222     }
0223 }