mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
shell: Rework configuration implementation
This reworks the implementation of the applet/containment configuration so that it is more optimized for the mobile experience and fixes lateral navigation (between categories). Changes: - Always show a list of category modules (switching from the navigation tab bar) in order to support more modules at once - Split the wallpaper and containment switching view into two modules - Add a close button at the top - Add an animation when the window opens and closes - Refactor the code so that it is clear which files are imported by the shell, and which are implementation details
This commit is contained in:
parent
c8479ca16d
commit
a39401100f
9 changed files with 330 additions and 343 deletions
|
|
@ -12,14 +12,18 @@ import org.kde.kirigami 2.19 as Kirigami
|
|||
import org.kde.plasma.configuration 2.0
|
||||
import org.kde.kitemmodels 1.0 as KItemModels
|
||||
|
||||
Rectangle {
|
||||
import './private'
|
||||
|
||||
/**
|
||||
* This component is loaded by libplasma when the "configuration window" is requested for an applet.
|
||||
*/
|
||||
Item {
|
||||
id: root
|
||||
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
color: "transparent"
|
||||
|
||||
//BEGIN properties
|
||||
// Properties filled in or needed by libplasma
|
||||
|
||||
property bool isContainment: false
|
||||
property alias app: appLoader.item
|
||||
|
|
@ -47,105 +51,46 @@ Rectangle {
|
|||
|
||||
//END model
|
||||
|
||||
//BEGIN functions
|
||||
|
||||
function saveConfig() {
|
||||
if (app.pageStack.currentItem.saveConfig) {
|
||||
app.pageStack.currentItem.saveConfig()
|
||||
}
|
||||
for (var key in Plasmoid.configuration) {
|
||||
if (app.pageStack.currentItem["cfg_"+key] !== undefined) {
|
||||
Plasmoid.configuration[key] = app.pageStack.currentItem["cfg_"+key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function configurationHasChanged() {
|
||||
for (var key in Plasmoid.configuration) {
|
||||
if (app.pageStack.currentItem["cfg_"+key] !== undefined) {
|
||||
//for objects == doesn't work
|
||||
if (typeof Plasmoid.configuration[key] == 'object') {
|
||||
for (var i in Plasmoid.configuration[key]) {
|
||||
if (Plasmoid.configuration[key][i] != app.pageStack.currentItem["cfg_"+key][i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (app.pageStack.currentItem["cfg_"+key] != Plasmoid.configuration[key]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function settingValueChanged() {
|
||||
if (app.pageStack.currentItem.saveConfig !== undefined) {
|
||||
app.pageStack.currentItem.saveConfig();
|
||||
} else {
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function open(item) {
|
||||
app.isAboutPage = false;
|
||||
if (item.source) {
|
||||
app.isAboutPage = item.source === "AboutPlugin.qml";
|
||||
pushReplace(Qt.resolvedUrl("ConfigurationAppletPage.qml"), {configItem: item, title: item.name});
|
||||
app.pageStack.push(Qt.resolvedUrl("private/ConfigurationAppletPage.qml"), {configItem: item, title: item.name});
|
||||
} else if (item.kcm) {
|
||||
pushReplace(configurationKcmPageComponent, {kcm: item.kcm, internalPage: item.kcm.mainUi});
|
||||
} else {
|
||||
app.pageStack.pop();
|
||||
app.pageStack.push(configurationKcmPageComponent, {kcm: item.kcm, internalPage: item.kcm.mainUi});
|
||||
}
|
||||
}
|
||||
|
||||
//END functions
|
||||
|
||||
|
||||
//BEGIN connections
|
||||
|
||||
Connections {
|
||||
target: root.Window.window
|
||||
function onVisibleChanged() {
|
||||
if (root.Window.window.visible) {
|
||||
root.Window.window.showMaximized();
|
||||
Binding {
|
||||
// Window bindings
|
||||
root.Window.window.flags: Qt.FramelessWindowHint
|
||||
root.Window.window.visibility: Window.Maximized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//END connections
|
||||
|
||||
//BEGIN UI components
|
||||
|
||||
Component {
|
||||
id: configurationKcmPageComponent
|
||||
ConfigurationKcmPage {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: configListPageComponent
|
||||
ConfigListPage {
|
||||
onRequestOpen: (delegate) => root.open(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: appLoader
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
active: root.loadApp
|
||||
|
||||
// Load first page
|
||||
onLoaded: {
|
||||
// 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) {
|
||||
root.open(root.globalConfigModel.get(0))
|
||||
} else {
|
||||
root.open(configDialog.configModel.get(0))
|
||||
}
|
||||
// Push config list page
|
||||
app.pageStack.push(configListPageComponent, {
|
||||
title: i18nc("The title of the applet configuration window", "Configure %1", Plasmoid.metaData.name),
|
||||
model1: configDialogFilterModel,
|
||||
model2: root.globalConfigModel
|
||||
});
|
||||
|
||||
root.appLoaded();
|
||||
}
|
||||
|
|
@ -154,90 +99,50 @@ Rectangle {
|
|||
id: app
|
||||
anchors.fill: parent
|
||||
|
||||
// animation on show
|
||||
opacity: 0
|
||||
NumberAnimation on opacity {
|
||||
to: 1
|
||||
running: true
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
pageStack {
|
||||
globalToolBar {
|
||||
canContainHandles: true
|
||||
style: Kirigami.ApplicationHeaderStyle.ToolBar
|
||||
showNavigationButtons: Kirigami.ApplicationHeaderStyle.ShowBackButton
|
||||
}
|
||||
popHiddenPages: true
|
||||
columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn
|
||||
}
|
||||
|
||||
pageStack.globalToolBar.canContainHandles: true
|
||||
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar
|
||||
pageStack.globalToolBar.showNavigationButtons: Kirigami.ApplicationHeaderStyle.ShowBackButton;
|
||||
|
||||
property var currentConfigPage: null
|
||||
property bool isAboutPage: false
|
||||
|
||||
// pop pages when not in use
|
||||
// Implement open/close animation
|
||||
Connections {
|
||||
target: app.pageStack
|
||||
function onCurrentIndexChanged() {
|
||||
// wait for animation to finish before popping pages
|
||||
timer.restart();
|
||||
target: root.Window.window
|
||||
|
||||
function onVisibleChanged() {
|
||||
if (visible) {
|
||||
opacityAnim.to = 1;
|
||||
opacityAnim.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 300
|
||||
onTriggered: {
|
||||
let currentIndex = app.pageStack.currentIndex;
|
||||
while (app.pageStack.depth > (currentIndex + 1) && currentIndex >= 0) {
|
||||
app.pageStack.pop();
|
||||
function onClosing(close) {
|
||||
if (app.opacity !== 0) {
|
||||
close.accepted = false;
|
||||
opacityAnim.to = 0;
|
||||
opacityAnim.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: Kirigami.NavigationTabBar {
|
||||
id: footerBar
|
||||
visible: count > 1
|
||||
height: visible ? implicitHeight : 0
|
||||
Repeater {
|
||||
model: root.isContainment ? globalConfigModel : undefined
|
||||
delegate: configCategoryDelegate
|
||||
opacity: 0
|
||||
scale: 0.7 + 0.3 * app.opacity
|
||||
|
||||
NumberAnimation on opacity {
|
||||
id: opacityAnim
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.OutCubic
|
||||
onFinished: {
|
||||
if (app.opacity === 0) {
|
||||
root.Window.window.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: configDialogFilterModel
|
||||
delegate: configCategoryDelegate
|
||||
}
|
||||
Repeater {
|
||||
model: !root.isContainment ? globalConfigModel : undefined
|
||||
delegate: configCategoryDelegate
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: configCategoryDelegate
|
||||
Kirigami.NavigationTabButton {
|
||||
icon.name: model.icon
|
||||
text: model.name
|
||||
width: footerBar.buttonWidth
|
||||
QQC2.ButtonGroup.group: footerBar.tabGroup
|
||||
|
||||
onClicked: {
|
||||
if (checked) {
|
||||
root.open(model);
|
||||
}
|
||||
}
|
||||
|
||||
checked: {
|
||||
if (app.pageStack.currentItem) {
|
||||
if (model.kcm && app.pageStack.currentItem.kcm) {
|
||||
return model.kcm == app.pageStack.currentItem.kcm;
|
||||
} else if (app.pageStack.currentItem.configItem) {
|
||||
return model.source == app.pageStack.currentItem.configItem.source;
|
||||
} else {
|
||||
return app.pageStack.currentItem.source == Qt.resolvedUrl(model.source);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//END UI components
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami 2.10 as Kirigami
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
title: configItem.name
|
||||
|
||||
required property var configItem
|
||||
|
||||
signal settingValueChanged()
|
||||
onSettingValueChanged: saveConfig() // we save config immediately on mobile
|
||||
|
||||
function saveConfig() {
|
||||
for (let key in Plasmoid.configuration) {
|
||||
if (loader.item["cfg_" + key] != undefined) {
|
||||
Plasmoid.configuration[key] = loader.item["cfg_" + key]
|
||||
}
|
||||
}
|
||||
|
||||
// For ConfigurationContainmentActions.qml
|
||||
if (loader.item.hasOwnProperty("saveConfig")) {
|
||||
loader.item.saveConfig()
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: loader.height
|
||||
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
bottomPadding: 0
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
width: parent.width
|
||||
// HACK the height of the loader is based on the implicitHeight of the content.
|
||||
// Unfortunately not all content items have a sensible implicitHeight.
|
||||
// If it is zero fall back to the height of its children
|
||||
// Also make it at least as high as the page itself. Some existing configs assume they fill the whole space
|
||||
// TODO KF6 clean this up by making all configs based on SimpleKCM/ScrollViewKCM/GridViewKCM
|
||||
height: {
|
||||
if (item) {
|
||||
return Math.max(root.availableHeight, item.implicitHeight ? item.implicitHeight : item.childrenRect.height);
|
||||
} else {
|
||||
return root.availableHeight;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const plasmoidConfig = Plasmoid.configuration
|
||||
|
||||
const props = {}
|
||||
for (let key in plasmoidConfig) {
|
||||
props["cfg_" + key] = Plasmoid.configuration[key]
|
||||
}
|
||||
|
||||
setSource(configItem.source, props)
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
const plasmoidConfig = Plasmoid.configuration;
|
||||
|
||||
for (let key in plasmoidConfig) {
|
||||
const changedSignal = item["cfg_" + key + "Changed"]
|
||||
if (changedSignal) {
|
||||
changedSignal.connect(root.settingValueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
const configurationChangedSignal = item.configurationChanged
|
||||
if (configurationChangedSignal) {
|
||||
configurationChangedSignal.connect(root.settingValueChanged)
|
||||
}
|
||||
|
||||
var unsavedChangesChangedSignal = item.unsavedChangesChanged
|
||||
if (unsavedChangesChangedSignal) {
|
||||
unsavedChangesChangedSignal.connect( () => {
|
||||
if (item.unsavedChanges) {
|
||||
root.settingValueChanged()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,27 +13,29 @@ import org.kde.plasma.core as PlasmaCore
|
|||
import org.kde.plasma.configuration 2.0
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
|
||||
/**
|
||||
* This component is loaded by libplasma when the "configuration window" is requested for a containment.
|
||||
*/
|
||||
AppletConfiguration {
|
||||
id: root
|
||||
isContainment: true
|
||||
loadApp: true
|
||||
|
||||
readonly property bool horizontal: root.width > root.height
|
||||
|
||||
onAppLoaded: {
|
||||
app.width = root.width < root.height ? root.width : Math.min(root.width, Math.max(app.implicitWidth, Kirigami.Units.gridUnit * 45));
|
||||
app.height = Math.min(root.height, Math.max(app.implicitHeight, Kirigami.Units.gridUnit * 29));
|
||||
}
|
||||
|
||||
//BEGIN model
|
||||
globalConfigModel: globalContainmentConfigModel
|
||||
|
||||
ConfigModel {
|
||||
id: globalContainmentConfigModel
|
||||
ConfigCategory {
|
||||
name: i18nd("plasma_shell_org.kde.plasma.desktop", "Wallpaper")
|
||||
icon: "preferences-desktop-wallpaper"
|
||||
source: "ConfigurationContainmentAppearance.qml"
|
||||
name: i18n("Wallpaper")
|
||||
icon: "viewimage-symbolic"
|
||||
source: "ChangeWallpaperModule.qml" // This is a relative path from inside private (since loading is invoked from there)
|
||||
}
|
||||
ConfigCategory {
|
||||
name: i18n("Change Homescreen")
|
||||
icon: "exchange-positions"
|
||||
source: "ChangeContainmentModule.qml" // This is a relative path from inside private (since loading is invoked from there)
|
||||
visible: configDialog.containmentPluginsConfigModel.count > 1
|
||||
}
|
||||
}
|
||||
//END model
|
||||
|
|
|
|||
8
shell/contents/configuration/README.md
Normal file
8
shell/contents/configuration/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: None
|
||||
- SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
This folder contains source files for implementing the configuration window for applets/containments (including homescreens).
|
||||
|
||||
[libplasma](https://invent.kde.org/frameworks/libplasma) loads either `AppletConfiguration.qml` or `ContainmentConfiguration.qml` (depending on if its an applet or containment) from this folder when requested by the shell, which in turn initializes the config window.
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.configuration
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property string containmentPlugin: configDialog.containmentPlugin
|
||||
signal configurationChanged // No need to emit, because containment changes apply immediately
|
||||
|
||||
//BEGIN functions
|
||||
function saveConfig() {
|
||||
configDialog.containmentPlugin = root.containmentPlugin
|
||||
}
|
||||
//END functions
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Select Homescreen")
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: configDialog.containmentPluginsConfigModel
|
||||
delegate: FormCard.FormRadioDelegate {
|
||||
enabled: !Plasmoid.immutable
|
||||
text: model.name
|
||||
checked: configDialog.containmentPlugin === model.pluginName
|
||||
|
||||
// Always restore binding
|
||||
onCheckedChanged: checked = Qt.binding(() => configDialog.containmentPlugin === model.pluginName);
|
||||
|
||||
onClicked: {
|
||||
if (root.containmentPlugin === model.pluginName) {
|
||||
return;
|
||||
}
|
||||
root.containmentPlugin = model.pluginName;
|
||||
confirmationDialog.name = model.name;
|
||||
confirmationDialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.PromptDialog {
|
||||
id: confirmationDialog
|
||||
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
|
||||
|
||||
property string name
|
||||
|
||||
title: i18n("Change homescreen to %1?", name)
|
||||
subtitle: i18n("Your current homescreen's settings are saved, and will be restored if you switch back.")
|
||||
|
||||
onAccepted: {
|
||||
root.saveConfig();
|
||||
close();
|
||||
}
|
||||
onRejected: {
|
||||
root.containmentPlugin = configDialog.containmentPlugin;
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
// SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import org.kde.plasma.configuration 2.0
|
||||
|
|
@ -19,7 +17,6 @@ ColumnLayout {
|
|||
spacing: 0
|
||||
|
||||
property string currentWallpaper: ""
|
||||
property string containmentPlugin: configDialog.containmentPlugin
|
||||
signal configurationChanged
|
||||
|
||||
//BEGIN functions
|
||||
|
|
@ -34,59 +31,17 @@ ColumnLayout {
|
|||
}
|
||||
configDialog.currentWallpaper = root.currentWallpaper;
|
||||
configDialog.applyWallpaper()
|
||||
configDialog.containmentPlugin = root.containmentPlugin
|
||||
}
|
||||
//END functions
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Plasmoid.immutable || animating
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Layout changes have been restricted by the system administrator")
|
||||
showCloseButton: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 2 // we need this because ColumnLayout's spacing is 0
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: generalConfig
|
||||
spacing: 0
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("General")
|
||||
}
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
FormCard.FormCard {
|
||||
FormCard.FormComboBoxDelegate {
|
||||
id: layoutSelectComboBox
|
||||
enabled: !Plasmoid.immutable
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Homescreen Layout")
|
||||
description: i18n("The homescreen layout to use.")
|
||||
visible: model.count > 1 // only show if there are multiple plugins
|
||||
|
||||
model: configDialog.containmentPluginsConfigModel
|
||||
textRole: "name"
|
||||
valueRole: "pluginName"
|
||||
currentIndex: determineCurrentIndex()
|
||||
onCurrentIndexChanged: {
|
||||
root.containmentPlugin = configDialog.containmentPluginsConfigModel.get(currentIndex).pluginName;
|
||||
}
|
||||
|
||||
function determineCurrentIndex() {
|
||||
for (var i = 0; i < configDialog.containmentPluginsConfigModel.count; ++i) {
|
||||
var data = configDialog.containmentPluginsConfigModel.get(i);
|
||||
if (configDialog.containmentPlugin === data.pluginName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator { above: layoutSelectComboBox; below: wallpaperPluginSelectComboBox }
|
||||
|
||||
FormCard.FormComboBoxDelegate {
|
||||
id: wallpaperPluginSelectComboBox
|
||||
|
|
@ -138,30 +93,6 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: switchContainmentWarning
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
visible: configDialog.containmentPlugin !== root.containmentPlugin
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Layout changes must be applied before other changes can be made")
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply now")
|
||||
onClicked: saveConfig()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: switchContainmentWarning.visible
|
||||
visible: switchContainmentWarning.visible
|
||||
}
|
||||
|
||||
Item {
|
||||
id: emptyConfig
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
|
@ -172,11 +103,9 @@ ColumnLayout {
|
|||
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
Layout.maximumHeight: root.height - generalConfig.height - 70 // HACK: wallpaper configs seem to go over the provisioned height
|
||||
Layout.maximumHeight: root.height - generalConfig.height - Kirigami.Units.smallSpacing // HACK: wallpaper configs seem to go over the provisioned height
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: !switchContainmentWarning.visible
|
||||
|
||||
// Bug 360862: if wallpaper has no config, sourceFile will be ""
|
||||
// so we wouldn't load emptyConfig and break all over the place
|
||||
// hence set it to some random value initially
|
||||
77
shell/contents/configuration/private/ConfigListPage.qml
Normal file
77
shell/contents/configuration/private/ConfigListPage.qml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.configuration
|
||||
import org.kde.kitemmodels as KItemModels
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
property alias model1: repeater1.model
|
||||
property alias model2: repeater2.model
|
||||
|
||||
topPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
titleDelegate: RowLayout {
|
||||
// Add close button
|
||||
QQC2.ToolButton {
|
||||
Layout.leftMargin: -Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
|
||||
icon.name: "arrow-left"
|
||||
onClicked: root.Window.window.close()
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
level: 1
|
||||
text: root.title
|
||||
}
|
||||
}
|
||||
|
||||
signal requestOpen(var delegate)
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Plasmoid.immutable
|
||||
text: i18n("Layout changes have been restricted by the system administrator")
|
||||
showCloseButton: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 2 // we need this because ColumnLayout's spacing is 0
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater1
|
||||
|
||||
delegate: QQC2.ItemDelegate {
|
||||
icon.name: model.icon
|
||||
text: model.name
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: root.requestOpen(model)
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater2
|
||||
|
||||
delegate: QQC2.ItemDelegate {
|
||||
icon.name: model.icon
|
||||
text: model.name
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: root.requestOpen(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami 2.10 as Kirigami
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
required property var configItem
|
||||
|
||||
signal settingValueChanged()
|
||||
onSettingValueChanged: saveConfig() // we save config immediately on mobile
|
||||
|
||||
title: configItem.name
|
||||
|
||||
topPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
function saveConfig() {
|
||||
for (let key in Plasmoid.configuration) {
|
||||
if (loader.item["cfg_" + key] != undefined) {
|
||||
Plasmoid.configuration[key] = loader.item["cfg_" + key]
|
||||
}
|
||||
}
|
||||
|
||||
// For ConfigurationContainmentActions.qml
|
||||
if (loader.item.hasOwnProperty("saveConfig")) {
|
||||
loader.item.saveConfig()
|
||||
}
|
||||
}
|
||||
|
||||
data: [
|
||||
Loader {
|
||||
id: loader
|
||||
|
||||
Component.onCompleted: {
|
||||
const plasmoidConfig = Plasmoid.configuration
|
||||
|
||||
const props = {}
|
||||
for (let key in plasmoidConfig) {
|
||||
props["cfg_" + key] = Plasmoid.configuration[key]
|
||||
}
|
||||
|
||||
// Inject configurable config values
|
||||
setSource(configItem.source, props)
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
item.parent = root.contentItem;
|
||||
item.anchors.fill = root.contentItem;
|
||||
|
||||
const plasmoidConfig = Plasmoid.configuration;
|
||||
|
||||
for (let key in plasmoidConfig) {
|
||||
const changedSignal = item["cfg_" + key + "Changed"]
|
||||
if (changedSignal) {
|
||||
changedSignal.connect(root.settingValueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
const configurationChangedSignal = item.configurationChanged
|
||||
if (configurationChangedSignal) {
|
||||
configurationChangedSignal.connect(root.settingValueChanged)
|
||||
}
|
||||
|
||||
var unsavedChangesChangedSignal = item.unsavedChangesChanged
|
||||
if (unsavedChangesChangedSignal) {
|
||||
unsavedChangesChangedSignal.connect( () => {
|
||||
if (item.unsavedChanges) {
|
||||
root.settingValueChanged()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -16,11 +16,13 @@ Kirigami.Page {
|
|||
signal settingValueChanged()
|
||||
onSettingValueChanged: saveConfig(); // we save config immediately on mobile
|
||||
|
||||
title: kcm.name
|
||||
title: internalPage.title ? internalPage.title : kcm.name
|
||||
|
||||
topPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
flickable: internalPage.flickable
|
||||
actions: [
|
||||
internalPage.actions.main,
|
||||
|
|
@ -38,7 +40,7 @@ Kirigami.Page {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
kcm.load()
|
||||
kcm.load();
|
||||
}
|
||||
|
||||
function saveConfig() {
|
||||
|
|
@ -48,14 +50,21 @@ Kirigami.Page {
|
|||
data: [
|
||||
Connections {
|
||||
target: kcm
|
||||
onPagePushed: {
|
||||
function onPagePushed() {
|
||||
app.pageStack.push(configurationKcmPageComponent.createObject(app.pageStack, {"kcm": kcm, "internalPage": page}));
|
||||
}
|
||||
onPageRemoved: app.pageStack.pop();
|
||||
function onPageRemoved() {
|
||||
app.pageStack.pop();
|
||||
}
|
||||
function onNeedsSaveChanged() {
|
||||
if (kcm.needsSave) {
|
||||
container.settingValueChanged()
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections {
|
||||
target: app.pageStack
|
||||
onPageRemoved: {
|
||||
function onPageRemoved() {
|
||||
if (kcm.needsSave) {
|
||||
kcm.save()
|
||||
}
|
||||
|
|
@ -65,12 +74,4 @@ Kirigami.Page {
|
|||
}
|
||||
}
|
||||
]
|
||||
Connections {
|
||||
target: kcm
|
||||
function onNeedsSaveChanged() {
|
||||
if (kcm.needsSave) {
|
||||
container.settingValueChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue