aerothemeplasma/plasma/shells/org.kde.plasma.desktop/contents/configuration/AppletConfiguration.qml
2024-08-09 03:20:25 +02:00

514 lines
16 KiB
QML

/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
SPDX-FileCopyrightText: 2022-2023 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels 1.0 as KItemModels
import org.kde.plasma.configuration 2.0
import org.kde.plasma.plasmoid 2.0
Rectangle {
id: root
implicitWidth: Kirigami.Units.gridUnit * 40
implicitHeight: Kirigami.Units.gridUnit * 30
Layout.minimumWidth: Kirigami.Units.gridUnit * 30
Layout.minimumHeight: Kirigami.Units.gridUnit * 21
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
color: Kirigami.Theme.backgroundColor
property bool isContainment: false
property ConfigModel globalConfigModel: globalAppletConfigModel
property url currentSource
function closing() {
if (applyButton.enabled) {
messageDialog.item = null;
messageDialog.open();
return false;
}
return true;
}
function saveConfig() {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
config.keys().forEach(key => {
const cfgKey = "cfg_" + key;
if (cfgKey in app.pageStack.currentItem) {
config[key] = app.pageStack.currentItem[cfgKey];
}
})
plasmoid.configuration.writeConfig();
// For ConfigurationContainmentActions.qml
if (app.pageStack.currentItem.hasOwnProperty("saveConfig")) {
app.pageStack.currentItem.saveConfig()
}
}
Connections {
target: configDialog
function onClosing(event) {
event.accepted = closing();
}
}
ConfigModel {
id: globalAppletConfigModel
ConfigCategory {
name: i18nd("plasma_shell_org.kde.plasma.desktop", "Keyboard Shortcuts")
icon: "preferences-desktop-keyboard"
source: Qt.resolvedUrl("ConfigurationShortcuts.qml")
}
}
KItemModels.KSortFilterProxyModel {
id: configDialogFilterModel
sourceModel: configDialog.configModel
filterRowCallback: (row, parent) => {
return sourceModel.data(sourceModel.index(row, 0), ConfigModel.VisibleRole);
}
}
function settingValueChanged() {
applyButton.enabled = true;
}
function pushReplace(item, config) {
let page;
if (app.pageStack.depth === 0) {
page = app.pageStack.push(item, config);
} else {
page = app.pageStack.replace(item, config);
}
app.currentConfigPage = page;
}
Component {
id: configurationKcmPageComponent
ConfigurationKcmPage {
}
}
function open(item) {
app.isAboutPage = false;
root.currentSource = item.source
if (item.source) {
app.isAboutPage = item.source === Qt.resolvedUrl("AboutPlugin.qml");
if (isContainment) {
pushReplace(Qt.resolvedUrl("ConfigurationAppletPage.qml"), {configItem: item});
} else {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
const props = {
"title": item.name
};
config.keys().forEach(key => {
props["cfg_" + key] = config[key];
});
pushReplace(item.source, props);
}
} else if (item.kcm) {
pushReplace(configurationKcmPageComponent, {kcm: item.kcm, internalPage: item.kcm.mainUi});
} else {
app.pageStack.pop();
}
applyButton.enabled = false
}
Connections {
target: app.currentConfigPage
function onSettingValueChanged() {
applyButton.enabled = true;
}
}
Connections {
target: app.pageStack
function onCurrentItemChanged() {
if (app.pageStack.currentItem !== null && !isContainment) {
const config = Plasmoid.configuration; // type: KConfigPropertyMap
config.keys().forEach(key => {
const changedSignal = app.pageStack.currentItem["cfg_" + key + "Changed"];
if (changedSignal) {
changedSignal.connect(() => root.settingValueChanged());
}
});
const configurationChangedSignal = app.pageStack.currentItem.configurationChanged;
if (configurationChangedSignal) {
configurationChangedSignal.connect(() => root.settingValueChanged());
}
}
}
}
Component.onCompleted: {
// if we are a containment then the first item will be ConfigurationContainmentAppearance
// if the applet does not have own configs then the first item will be Shortcuts
if (isContainment || !configDialog.configModel || configDialog.configModel.count === 0) {
open(root.globalConfigModel.get(0))
} else {
open(configDialog.configModel.get(0))
}
}
function applicationWindow() {
return app;
}
QQC2.ScrollView {
id: categoriesScroll
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: Kirigami.Units.gridUnit * 10
contentWidth: availableWidth
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
activeFocusOnTab: true
focus: true
Accessible.role: Accessible.MenuBar
background: Rectangle {
color: Kirigami.Theme.backgroundColor
}
Image {
source: "rsrc/bg.png"
anchors.fill: parent
smooth: true
}
Keys.onUpPressed: event => {
const buttons = categories.children
let foundPrevious = false
for (let i = buttons.length - 1; i >= 0; --i) {
const button = buttons[i];
if (!button.hasOwnProperty("highlighted")) {
// not a ConfigCategoryDelegate
continue;
}
if (foundPrevious) {
categories.openCategory(button.item)
categoriesScroll.forceActiveFocus(Qt.TabFocusReason)
return
} else if (button.highlighted) {
foundPrevious = true
}
}
event.accepted = false
}
Keys.onDownPressed: event => {
const buttons = categories.children
let foundNext = false
for (let i = 0, length = buttons.length; i < length; ++i) {
const button = buttons[i];
if (!button.hasOwnProperty("highlighted")) {
continue;
}
if (foundNext) {
categories.openCategory(button.item)
categoriesScroll.forceActiveFocus(Qt.TabFocusReason)
return
} else if (button.highlighted) {
foundNext = true
}
}
event.accepted = false
}
ColumnLayout {
id: categories
spacing: Kirigami.Units.largeSpacing
width: categoriesScroll.contentWidth
focus: true
function openCategory(item) {
if (applyButton.enabled) {
messageDialog.item = item;
messageDialog.open();
return;
}
open(item)
}
Item {
id: paddingItem
Layout.preferredHeight: Kirigami.Units.iconSizes.small
}
Component {
id: categoryDelegate
RowLayout {
id: delegate
required property var model
property var item: model
Accessible.role: Accessible.MenuItem
Accessible.name: model.name
Accessible.description: i18nd("plasma_shell_org.kde.plasma.desktop", "Open configuration page")
Accessible.onPressAction: ma.clicked(null)
Layout.leftMargin: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing
property bool highlighted: {
if (app.pageStack.currentItem) {
if (model.kcm && app.pageStack.currentItem.kcm) {
return model.kcm == app.pageStack.currentItem.kcm
} else {
return root.currentSource == model.source
}
}
return false
}
Kirigami.Icon {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
implicitWidth: Kirigami.Units.iconSizes.small
implicitHeight: Kirigami.Units.iconSizes.small
source: delegate.model.icon
}
Text {
color: ma.containsMouse ? "#074ae5" : "#151c55"
font.underline: ma.containsMouse
font.bold: parent.highlighted || delegate.focus
text: parent.model.name
wrapMode: Text.WordWrap
MouseArea {
id: ma
anchors.fill: parent
enabled: !highlighted
hoverEnabled: true
onClicked: categories.openCategory(model);
cursorShape: delegate.highlighted ? Qt.ArrowCursor : Qt.PointingHandCursor
}
}
}
/*ConfigCategoryDelegate {
id: delegate
onActivated: categories.openCategory(model);
highlighted: {
if (app.pageStack.currentItem) {
if (model.kcm && app.pageStack.currentItem.kcm) {
return model.kcm == app.pageStack.currentItem.kcm
} else {
return root.currentSource == model.source
}
}
return false
}
item: model
}*/
}
Repeater {
Layout.fillWidth: true
model: root.isContainment ? globalConfigModel : undefined
delegate: categoryDelegate
}
Repeater {
Layout.fillWidth: true
model: configDialogFilterModel
delegate: categoryDelegate
}
Repeater {
Layout.fillWidth: true
model: !root.isContainment ? globalConfigModel : undefined
delegate: categoryDelegate
}
Repeater {
Layout.fillWidth: true
model: ConfigModel {
ConfigCategory{
name: i18nd("plasma_shell_org.kde.plasma.desktop", "About")
icon: "help-about"
source: Qt.resolvedUrl("AboutPlugin.qml")
}
}
delegate: categoryDelegate
}
}
}
Kirigami.Separator {
anchors {
left: parent.left
right: parent.right
top: parent.top
}
z: 1
}
Kirigami.Separator {
id: verticalSeparator
anchors {
top: parent.top
left: categoriesScroll.right
bottom: parent.bottom
}
z: 1
}
Kirigami.ApplicationItem {
id: app
anchors {
top: parent.top
left: verticalSeparator.right
right: parent.right
bottom: parent.bottom
}
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Breadcrumb
wideScreen: true
pageStack.globalToolBar.separatorVisible: bottomSeparator.visible
pageStack.globalToolBar.colorSet: Kirigami.Theme.View
property var currentConfigPage: null
onCurrentConfigPageChanged: {
if(currentConfigPage) {
currentConfigPage.Kirigami.Theme.colorSet = Kirigami.Theme.View
}
}
property bool isAboutPage: false
Kirigami.PromptDialog {
id: messageDialog
property var item
title: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply Settings")
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?")
standardButtons: Kirigami.Dialog.Apply | Kirigami.Dialog.Discard | Kirigami.Dialog.Cancel
onApplied: {
applyAction.trigger()
discarded();
}
onDiscarded: {
if (item) {
root.open(item);
messageDialog.close();
} else {
applyButton.enabled = false;
configDialog.close();
}
}
}
footer: QQC2.Pane {
padding: Kirigami.Units.largeSpacing
contentItem: RowLayout {
id: buttonsRow
spacing: Kirigami.Units.smallSpacing
Kirigami.Theme.colorSet: Kirigami.Theme.View
Item {
Layout.fillWidth: true
}
QQC2.Button {
icon.name: "dialog-ok"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "OK")
onClicked: acceptAction.trigger()
KeyNavigation.tab: categories
}
QQC2.Button {
id: applyButton
enabled: false
icon.name: "dialog-ok-apply"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply")
visible: !app.isAboutPage && app.pageStack.currentItem && (!app.pageStack.currentItem.kcm || app.pageStack.currentItem.kcm.buttons & 4) // 4 = Apply button
onClicked: applyAction.trigger()
}
QQC2.Button {
icon.name: "dialog-cancel"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Cancel")
onClicked: cancelAction.trigger()
visible: !app.isAboutPage
}
}
background: Item {
Kirigami.Separator {
id: bottomSeparator
visible: app.pageStack.currentItem
&& app.pageStack.currentItem.flickable
&& !(app.pageStack.currentItem.flickable.atYBeginning
&& app.pageStack.currentItem.flickable.atYEnd)
anchors {
left: parent.left
right: parent.right
top: parent.top
}
}
}
}
QQC2.Action {
id: acceptAction
onTriggered: {
applyAction.trigger();
configDialog.close();
}
}
QQC2.Action {
id: applyAction
onTriggered: {
if (isContainment) {
app.pageStack.get(0).saveConfig()
} else {
root.saveConfig()
}
applyButton.enabled = false;
}
}
QQC2.Action {
id: cancelAction
onTriggered: {
if (root.closing()) {
configDialog.close();
}
}
}
Keys.onReturnPressed: acceptAction.trigger();
Keys.onEscapePressed: cancelAction.trigger();
}
}