Warning, /plasma/plasma-desktop/desktoppackage/contents/configuration/AppletConfiguration.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
0003     SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
0004     SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
0005     SPDX-FileCopyrightText: 2022-2023 ivan tkachenko <me@ratijas.tk>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 import QtQuick 2.15
0011 import QtQuick.Controls 2.15 as QQC2
0012 import QtQuick.Layouts 1.15
0013 
0014 import org.kde.kirigami as Kirigami
0015 import org.kde.kitemmodels 1.0 as KItemModels
0016 import org.kde.plasma.configuration 2.0
0017 import org.kde.plasma.plasmoid 2.0
0018 
0019 Rectangle {
0020     id: root
0021 
0022     implicitWidth: Kirigami.Units.gridUnit * 40
0023     implicitHeight: Kirigami.Units.gridUnit * 30
0024 
0025     Layout.minimumWidth: Kirigami.Units.gridUnit * 30
0026     Layout.minimumHeight: Kirigami.Units.gridUnit * 21
0027 
0028     LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
0029     LayoutMirroring.childrenInherit: true
0030 
0031     color: Kirigami.Theme.backgroundColor
0032 
0033     property bool isContainment: false
0034 
0035     property ConfigModel globalConfigModel:  globalAppletConfigModel
0036 
0037     property url currentSource
0038 
0039     function closing() {
0040         if (applyButton.enabled) {
0041             messageDialog.item = null;
0042             messageDialog.open();
0043             return false;
0044         }
0045         return true;
0046     }
0047 
0048     function saveConfig() {
0049         const config = Plasmoid.configuration; // type: KConfigPropertyMap
0050 
0051         config.keys().forEach(key => {
0052             const cfgKey = "cfg_" + key;
0053             if (cfgKey in app.pageStack.currentItem) {
0054                 config[key] = app.pageStack.currentItem[cfgKey];
0055             }
0056         })
0057 
0058         plasmoid.configuration.writeConfig();
0059 
0060         // For ConfigurationContainmentActions.qml
0061         if (app.pageStack.currentItem.hasOwnProperty("saveConfig")) {
0062             app.pageStack.currentItem.saveConfig()
0063         }
0064     }
0065 
0066     Connections {
0067         target: configDialog
0068         function onClosing(event) {
0069             event.accepted = closing();
0070         }
0071     }
0072 
0073     ConfigModel {
0074         id: globalAppletConfigModel
0075         ConfigCategory {
0076             name: i18nd("plasma_shell_org.kde.plasma.desktop", "Keyboard Shortcuts")
0077             icon: "preferences-desktop-keyboard"
0078             source: Qt.resolvedUrl("ConfigurationShortcuts.qml")
0079         }
0080     }
0081 
0082     KItemModels.KSortFilterProxyModel {
0083         id: configDialogFilterModel
0084         sourceModel: configDialog.configModel
0085         filterRowCallback: (row, parent) => {
0086             return sourceModel.data(sourceModel.index(row, 0), ConfigModel.VisibleRole);
0087         }
0088     }
0089 
0090     function settingValueChanged() {
0091         applyButton.enabled = true;
0092     }
0093 
0094     function pushReplace(item, config) {
0095         let page;
0096         if (app.pageStack.depth === 0) {
0097             page = app.pageStack.push(item, config);
0098         } else {
0099             page = app.pageStack.replace(item, config);
0100         }
0101         app.currentConfigPage = page;
0102     }
0103     Component {
0104         id: configurationKcmPageComponent
0105         ConfigurationKcmPage {
0106         }
0107     }
0108 
0109     function open(item) {
0110         app.isAboutPage = false;
0111         root.currentSource = item.source
0112 
0113         if (item.source) {
0114             app.isAboutPage = item.source === Qt.resolvedUrl("AboutPlugin.qml");
0115 
0116             if (isContainment) {
0117                 pushReplace(Qt.resolvedUrl("ConfigurationAppletPage.qml"), {configItem: item});
0118             } else {
0119 
0120                 const config = Plasmoid.configuration; // type: KConfigPropertyMap
0121 
0122                 const props = {
0123                     "title": item.name
0124                 };
0125 
0126                 config.keys().forEach(key => {
0127                     props["cfg_" + key] = config[key];
0128                 });
0129 
0130                 pushReplace(item.source, props);
0131             }
0132 
0133         } else if (item.kcm) {
0134             pushReplace(configurationKcmPageComponent, {kcm: item.kcm, internalPage: item.kcm.mainUi});
0135         } else {
0136             app.pageStack.pop();
0137         }
0138 
0139         applyButton.enabled = false
0140     }
0141 
0142     Connections {
0143         target: app.currentConfigPage
0144 
0145         function onSettingValueChanged() {
0146             applyButton.enabled = true;
0147         }
0148     }
0149 
0150     Connections {
0151         target: app.pageStack
0152 
0153         function onCurrentItemChanged() {
0154             if (app.pageStack.currentItem !== null && !isContainment) {
0155                 const config = Plasmoid.configuration; // type: KConfigPropertyMap
0156 
0157                 config.keys().forEach(key => {
0158                     const changedSignal = app.pageStack.currentItem["cfg_" + key + "Changed"];
0159                     if (changedSignal) {
0160                         changedSignal.connect(() => root.settingValueChanged());
0161                     }
0162                 });
0163 
0164                 const configurationChangedSignal = app.pageStack.currentItem.configurationChanged;
0165                 if (configurationChangedSignal) {
0166                     configurationChangedSignal.connect(() => root.settingValueChanged());
0167                 }
0168             }
0169         }
0170     }
0171 
0172     Component.onCompleted: {
0173         // if we are a containment then the first item will be ConfigurationContainmentAppearance
0174         // if the applet does not have own configs then the first item will be Shortcuts
0175         if (isContainment || !configDialog.configModel || configDialog.configModel.count === 0) {
0176             open(root.globalConfigModel.get(0))
0177         } else {
0178             open(configDialog.configModel.get(0))
0179         }
0180     }
0181 
0182     function applicationWindow() {
0183         return app;
0184     }
0185 
0186 
0187     QQC2.ScrollView {
0188         id: categoriesScroll
0189         anchors {
0190             left: parent.left
0191             top: parent.top
0192             bottom: parent.bottom
0193         }
0194         width: Kirigami.Units.gridUnit * 7
0195         contentWidth: availableWidth
0196         Kirigami.Theme.colorSet: Kirigami.Theme.View
0197         Kirigami.Theme.inherit: false
0198         activeFocusOnTab: true
0199         focus: true
0200         Accessible.role: Accessible.MenuBar
0201         background: Rectangle {
0202             color: Kirigami.Theme.backgroundColor
0203         }
0204 
0205         Keys.onUpPressed: {
0206             const buttons = categories.children
0207 
0208             let foundPrevious = false
0209             for (let i = buttons.length - 1; i >= 0; --i) {
0210                 const button = buttons[i];
0211                 if (!button.hasOwnProperty("highlighted")) {
0212                     // not a ConfigCategoryDelegate
0213                     continue;
0214                 }
0215 
0216                 if (foundPrevious) {
0217                     categories.openCategory(button.item)
0218                     categoriesScroll.forceActiveFocus(Qt.TabFocusReason)
0219                     return
0220                 } else if (button.highlighted) {
0221                     foundPrevious = true
0222                 }
0223             }
0224 
0225             event.accepted = false
0226         }
0227 
0228         Keys.onDownPressed: {
0229             const buttons = categories.children
0230 
0231             let foundNext = false
0232             for (let i = 0, length = buttons.length; i < length; ++i) {
0233                 const button = buttons[i];
0234                 if (!button.hasOwnProperty("highlighted")) {
0235                     continue;
0236                 }
0237 
0238                 if (foundNext) {
0239                     categories.openCategory(button.item)
0240                     categoriesScroll.forceActiveFocus(Qt.TabFocusReason)
0241                     return
0242                 } else if (button.highlighted) {
0243                     foundNext = true
0244                 }
0245             }
0246 
0247             event.accepted = false
0248         }
0249 
0250         ColumnLayout {
0251             id: categories
0252 
0253             spacing: 0
0254             width: categoriesScroll.contentWidth
0255             focus: true
0256 
0257             function openCategory(item) {
0258                 if (applyButton.enabled) {
0259                     messageDialog.item = item;
0260                     messageDialog.open();
0261                     return;
0262                 }
0263                 open(item)
0264             }
0265 
0266             Component {
0267                 id: categoryDelegate
0268                 ConfigCategoryDelegate {
0269                     id: delegate
0270                     onActivated: categories.openCategory(model);
0271                     highlighted: {
0272                         if (app.pageStack.currentItem) {
0273                             if (model.kcm && app.pageStack.currentItem.kcm) {
0274                                 return model.kcm == app.pageStack.currentItem.kcm
0275                             } else {
0276                                 return root.currentSource == model.source
0277                             }
0278                         }
0279                         return false
0280                     }
0281                     item: model
0282                 }
0283             }
0284 
0285             Repeater {
0286                 Layout.fillWidth: true
0287                 model: root.isContainment ? globalConfigModel : undefined
0288                 delegate: categoryDelegate
0289             }
0290             Repeater {
0291                 Layout.fillWidth: true
0292                 model: configDialogFilterModel
0293                 delegate: categoryDelegate
0294             }
0295             Repeater {
0296                 Layout.fillWidth: true
0297                 model: !root.isContainment ? globalConfigModel : undefined
0298                 delegate: categoryDelegate
0299             }
0300             Repeater {
0301                 Layout.fillWidth: true
0302                 model: ConfigModel {
0303                     ConfigCategory{
0304                         name: i18nd("plasma_shell_org.kde.plasma.desktop", "About")
0305                         icon: "help-about"
0306                         source: Qt.resolvedUrl("AboutPlugin.qml")
0307                     }
0308                 }
0309                 delegate: categoryDelegate
0310             }
0311         }
0312     }
0313 
0314     Kirigami.Separator {
0315         anchors {
0316             left: parent.left
0317             right: parent.right
0318             top: parent.top
0319         }
0320         z: 1
0321     }
0322     Kirigami.Separator {
0323         id: verticalSeparator
0324         anchors {
0325             top: parent.top
0326             left: categoriesScroll.right
0327             bottom: parent.bottom
0328         }
0329         z: 1
0330     }
0331 
0332     Kirigami.ApplicationItem {
0333         id: app
0334         anchors {
0335             top: parent.top
0336             left: verticalSeparator.right
0337             right: parent.right
0338             bottom: parent.bottom
0339         }
0340 
0341         pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Breadcrumb
0342         wideScreen: true
0343         pageStack.globalToolBar.separatorVisible: bottomSeparator.visible
0344         pageStack.globalToolBar.colorSet: Kirigami.Theme.Window
0345 
0346         property var currentConfigPage: null
0347         property bool isAboutPage: false
0348 
0349         Kirigami.PromptDialog {
0350             id: messageDialog
0351             property var item
0352             title: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply Settings")
0353             subtitle: i18nd("plasma_shell_org.kde.plasma.desktop", "The settings of the current module have changed. Do you want to apply the changes or discard them?")
0354             standardButtons: Kirigami.Dialog.Apply | Kirigami.Dialog.Discard | Kirigami.Dialog.Cancel
0355             onApplied: {
0356                 applyAction.trigger()
0357                 discarded();
0358             }
0359             onDiscarded: {
0360                 if (item) {
0361                     root.open(item);
0362                     messageDialog.close();
0363                 } else {
0364                     applyButton.enabled = false;
0365                     configDialog.close();
0366                 }
0367             }
0368         }
0369 
0370         footer: QQC2.Pane {
0371 
0372             padding: Kirigami.Units.largeSpacing
0373 
0374             contentItem: RowLayout {
0375                 id: buttonsRow
0376                 spacing: Kirigami.Units.smallSpacing
0377 
0378                 Item {
0379                     Layout.fillWidth: true
0380                 }
0381 
0382                 QQC2.Button {
0383                     icon.name: "dialog-ok"
0384                     text: i18nd("plasma_shell_org.kde.plasma.desktop", "OK")
0385                     onClicked: acceptAction.trigger()
0386                     KeyNavigation.tab: categories
0387                 }
0388                 QQC2.Button {
0389                     id: applyButton
0390                     enabled: false
0391                     icon.name: "dialog-ok-apply"
0392                     text: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply")
0393                     visible: !app.isAboutPage && app.pageStack.currentItem && (!app.pageStack.currentItem.kcm || app.pageStack.currentItem.kcm.buttons & 4) // 4 = Apply button
0394                     onClicked: applyAction.trigger()
0395                 }
0396                 QQC2.Button {
0397                     icon.name: "dialog-cancel"
0398                     text: i18nd("plasma_shell_org.kde.plasma.desktop", "Cancel")
0399                     onClicked: cancelAction.trigger()
0400                     visible: !app.isAboutPage
0401                 }
0402             }
0403             background: Item {
0404                 Kirigami.Separator {
0405                     id: bottomSeparator
0406                     visible: app.pageStack.currentItem
0407                         && app.pageStack.currentItem.flickable
0408                         && !(app.pageStack.currentItem.flickable.atYBeginning
0409                         && app.pageStack.currentItem.flickable.atYEnd)
0410                     anchors {
0411                         left: parent.left
0412                         right: parent.right
0413                         top: parent.top
0414                     }
0415                 }
0416             }
0417         }
0418 
0419         QQC2.Action {
0420             id: acceptAction
0421             onTriggered: {
0422                 applyAction.trigger();
0423                 configDialog.close();
0424             }
0425         }
0426 
0427         QQC2.Action {
0428             id: applyAction
0429             onTriggered: {
0430                 if (isContainment) {
0431                     app.pageStack.get(0).saveConfig()
0432                 } else {
0433                     root.saveConfig()
0434                 }
0435 
0436                 applyButton.enabled = false;
0437             }
0438         }
0439 
0440         QQC2.Action {
0441             id: cancelAction
0442             onTriggered: {
0443                 if (root.closing()) {
0444                     configDialog.close();
0445                 }
0446             }
0447         }
0448 
0449         Keys.onReturnPressed: acceptAction.trigger();
0450         Keys.onEscapePressed: cancelAction.trigger();
0451     }
0452 }