v0.2.2: Preferences page, About page, full icon set, redesigned permissions list
- Add a Preferences page (reachable from the hamburger menu) with a KColorSchemeManager-backed color scheme switcher - Add an About page reading from KAboutData - Ship a full hicolor icon set (16-128px + scalable) via ecm_install_icons instead of a single flat SVG - Restyle the permissions list after KDE System Settings' Audio page: Kirigami.ListSectionHeader per category, flat separated rows, no card borders - Explicitly declare Kirigami/KirigamiAddons/ColorScheme as CMake dependencies instead of relying on the QML import scanner alone - Add KDEClangFormat/KDEGitCommitHooks, a CMakePresets.json, and a REUSE + clang-format lint CI workflow - Reformat SPDX headers to include copyright, and tidy up a stray namespace inconsistency
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
add_executable(flatkontrol
|
||||
@@ -15,11 +17,14 @@ qt_add_qml_module(flatkontrol
|
||||
QML_FILES
|
||||
qml/Main.qml
|
||||
qml/PermissionsPage.qml
|
||||
qml/SettingsPage.qml
|
||||
SOURCES
|
||||
applicationsmodel.cpp
|
||||
applicationsmodel.h
|
||||
permissionscontroller.cpp
|
||||
permissionscontroller.h
|
||||
appcolorscheme.cpp
|
||||
appcolorscheme.h
|
||||
)
|
||||
|
||||
target_include_directories(flatkontrol PRIVATE ${CMAKE_BINARY_DIR})
|
||||
@@ -38,6 +43,8 @@ target_link_libraries(flatkontrol PRIVATE
|
||||
KF6::IconThemes
|
||||
KF6::Crash
|
||||
KF6::DBusAddons
|
||||
KF6::ColorScheme
|
||||
KF6::Kirigami
|
||||
)
|
||||
|
||||
install(TARGETS flatkontrol ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "appcolorscheme.h"
|
||||
|
||||
#include <KColorSchemeManager>
|
||||
|
||||
AppColorScheme::AppColorScheme(QObject *parent)
|
||||
: QObject(parent)
|
||||
, mSchemes(KColorSchemeManager::instance())
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractItemModel *AppColorScheme::colorSchemesModel()
|
||||
{
|
||||
return mSchemes->model();
|
||||
}
|
||||
|
||||
QString AppColorScheme::activeColorSchemeName() const
|
||||
{
|
||||
return mSchemes->activeSchemeName();
|
||||
}
|
||||
|
||||
void AppColorScheme::setActiveColorSchemeName(const QString &name)
|
||||
{
|
||||
if (name == activeColorSchemeName()) {
|
||||
return;
|
||||
}
|
||||
mSchemes->activateScheme(mSchemes->indexForScheme(name));
|
||||
Q_EMIT activeColorSchemeNameChanged();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class KColorSchemeManager;
|
||||
|
||||
/**
|
||||
* Thin QML-facing wrapper around KColorSchemeManager: exposes the list of
|
||||
* installed color schemes and the currently active one. KColorSchemeManager
|
||||
* autosaves the active scheme itself, so there is nothing else to persist.
|
||||
*/
|
||||
class AppColorScheme : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
Q_PROPERTY(QAbstractItemModel *colorSchemesModel READ colorSchemesModel CONSTANT)
|
||||
Q_PROPERTY(QString activeColorSchemeName READ activeColorSchemeName WRITE setActiveColorSchemeName NOTIFY activeColorSchemeNameChanged)
|
||||
|
||||
public:
|
||||
explicit AppColorScheme(QObject *parent = nullptr);
|
||||
|
||||
QAbstractItemModel *colorSchemesModel();
|
||||
QString activeColorSchemeName() const;
|
||||
void setActiveColorSchemeName(const QString &name);
|
||||
|
||||
Q_SIGNALS:
|
||||
void activeColorSchemeNameChanged();
|
||||
|
||||
private:
|
||||
KColorSchemeManager *mSchemes;
|
||||
};
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "applicationsmodel.h"
|
||||
|
||||
#include <KDirWatch>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "flatpakinstallations.h"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "flatpakinstallations.h"
|
||||
#include "keyfile.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "keyfile.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QMap>
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "flatkontrol-version.h"
|
||||
|
||||
#include "applicationsmodel.h"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "permissioncatalog.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "permissionscontroller.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "flatpakinstallations.h"
|
||||
@@ -187,7 +192,7 @@ private:
|
||||
static bool isFilesystemPreset(const QString &bareToken);
|
||||
|
||||
FlatpakInstallations m_installations;
|
||||
FlatKontrol::PortalsBackend m_portals;
|
||||
PortalsBackend m_portals;
|
||||
|
||||
QString m_appId;
|
||||
QString m_appName;
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "portalsbackend.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
@@ -9,9 +14,6 @@
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusReply>
|
||||
|
||||
namespace FlatKontrol
|
||||
{
|
||||
|
||||
static constexpr uint SUPPORTED_SERVICE_VERSION = 2;
|
||||
static const char *DBUS_PATH = "/org/freedesktop/impl/portal/PermissionStore";
|
||||
static const char *DBUS_IFACE = "org.freedesktop.impl.portal.PermissionStore";
|
||||
@@ -253,5 +255,3 @@ void PortalsBackend::reload()
|
||||
m_supported.clear();
|
||||
m_reasons.clear();
|
||||
}
|
||||
|
||||
} // namespace FlatKontrol
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
@@ -8,9 +13,6 @@
|
||||
|
||||
class QDBusInterface;
|
||||
|
||||
namespace FlatKontrol
|
||||
{
|
||||
|
||||
/// Tri-state (plus availability) of a portal permission, matching Flatseal.
|
||||
enum class PortalState {
|
||||
Unknown = 0,
|
||||
@@ -71,5 +73,3 @@ private:
|
||||
QHash<QString, bool> m_supported; // property -> supported
|
||||
QHash<QString, QString> m_reasons; // property -> reason
|
||||
};
|
||||
|
||||
} // namespace FlatKontrol
|
||||
|
||||
+45
-1
@@ -1,4 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
@@ -7,6 +12,7 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kitemmodels as KItemModels
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import io.github.toservetheking.FlatKontrol
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
@@ -19,6 +25,44 @@ Kirigami.ApplicationWindow {
|
||||
width: Kirigami.Units.gridUnit * 48
|
||||
height: Kirigami.Units.gridUnit * 34
|
||||
|
||||
globalDrawer: Kirigami.GlobalDrawer {
|
||||
isMenu: true
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:inmenu", "Preferences…")
|
||||
icon.name: "configure"
|
||||
onTriggered: root.pageStack.pushDialogLayer(settingsPageComponent, {
|
||||
width: root.width
|
||||
}, {
|
||||
width: Kirigami.Units.gridUnit * 24,
|
||||
height: Kirigami.Units.gridUnit * 20,
|
||||
modality: Qt.NonModal
|
||||
})
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:inmenu", "About %1", root.title)
|
||||
icon.name: "help-about"
|
||||
onTriggered: root.pageStack.pushDialogLayer(aboutPageComponent, {
|
||||
width: root.width
|
||||
}, {
|
||||
width: Kirigami.Units.gridUnit * 30,
|
||||
height: Kirigami.Units.gridUnit * 30,
|
||||
modality: Qt.NonModal
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Component {
|
||||
id: aboutPageComponent
|
||||
FormCard.AboutPage {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: settingsPageComponent
|
||||
SettingsPage {}
|
||||
}
|
||||
|
||||
ApplicationsModel {
|
||||
id: appsModel
|
||||
}
|
||||
|
||||
+159
-41
@@ -1,6 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
// Styled after KDE System Settings' Audio page (plasma-pa's kcm/ui/main.qml):
|
||||
// Kirigami.ListSectionHeader per category, flat rows indented under the
|
||||
// header and separated by Kirigami.Separator, no card/border around them.
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
@@ -8,7 +16,6 @@ import QtQuick.Dialogs
|
||||
import Qt.labs.qmlmodels
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kitemmodels as KItemModels
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import io.github.toservetheking.FlatKontrol
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
@@ -22,10 +29,12 @@ Kirigami.ScrollablePage {
|
||||
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
topPadding: Kirigami.Units.gridUnit
|
||||
// No topPadding: Kirigami.ListSectionHeader (the first thing on the
|
||||
// page) already carries its own top padding, and stacking ours on top
|
||||
// of that left an oversized gap above the first category.
|
||||
bottomPadding: Kirigami.Units.gridUnit
|
||||
|
||||
// A small status marker used across delegates.
|
||||
// A small status marker used across rows.
|
||||
component StatusBadge: Kirigami.Icon {
|
||||
property int state: 0
|
||||
visible: state > 0
|
||||
@@ -86,7 +95,7 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
spacing: 0
|
||||
visible: page.hasSelection
|
||||
|
||||
Repeater {
|
||||
@@ -100,20 +109,29 @@ Kirigami.ScrollablePage {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: section.modelData.title
|
||||
Kirigami.ListSectionHeader {
|
||||
Layout.fillWidth: true
|
||||
text: section.modelData.title
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
KItemModels.KSortFilterProxyModel {
|
||||
id: catModel
|
||||
sourceModel: page.controller
|
||||
filterRoleName: "categoryId"
|
||||
// No category id is a substring of another, so this is an exact match.
|
||||
filterString: section.modelData.id
|
||||
}
|
||||
KItemModels.KSortFilterProxyModel {
|
||||
id: catModel
|
||||
sourceModel: page.controller
|
||||
filterRoleName: "categoryId"
|
||||
// No category id is a substring of another, so this is an exact match.
|
||||
filterString: section.modelData.id
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Repeater {
|
||||
id: rowRepeater
|
||||
model: catModel
|
||||
|
||||
delegate: DelegateChooser {
|
||||
@@ -122,7 +140,7 @@ Kirigami.ScrollablePage {
|
||||
// Toggle
|
||||
DelegateChoice {
|
||||
roleValue: 0
|
||||
delegate: FormCard.FormSwitchDelegate {
|
||||
delegate: ColumnLayout {
|
||||
id: toggleD
|
||||
required property int index
|
||||
required property string label
|
||||
@@ -130,18 +148,51 @@ Kirigami.ScrollablePage {
|
||||
required property bool value
|
||||
required property int status
|
||||
readonly property int sourceRow: catModel.mapToSource(catModel.index(index, 0)).row
|
||||
text: label
|
||||
description: example
|
||||
checked: value
|
||||
icon.name: status === 2 ? "document-edit-symbolic" : (status === 1 ? "globe-symbolic" : "")
|
||||
onToggled: page.controller.setToggleValue(sourceRow, checked)
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: toggleD.label
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: toggleD.example
|
||||
visible: text.length > 0
|
||||
wrapMode: Text.Wrap
|
||||
font: Kirigami.Theme.smallFont
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
}
|
||||
StatusBadge {
|
||||
state: toggleD.status
|
||||
}
|
||||
QQC2.Switch {
|
||||
checked: toggleD.value
|
||||
onToggled: page.controller.setToggleValue(toggleD.sourceRow, checked)
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: toggleD.index !== rowRepeater.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filesystem path (with access mode + Browse)
|
||||
DelegateChoice {
|
||||
roleValue: 1
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
delegate: ColumnLayout {
|
||||
id: fsD
|
||||
required property int index
|
||||
required property string value
|
||||
@@ -149,7 +200,8 @@ Kirigami.ScrollablePage {
|
||||
required property int status
|
||||
required property bool removable
|
||||
readonly property int sourceRow: catModel.mapToSource(catModel.index(index, 0)).row
|
||||
background: null
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
FolderDialog {
|
||||
id: folderDialog
|
||||
@@ -160,7 +212,8 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
QQC2.TextField {
|
||||
id: fsField
|
||||
@@ -190,21 +243,30 @@ Kirigami.ScrollablePage {
|
||||
onClicked: page.controller.removeEntry(fsD.sourceRow)
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: fsD.index !== rowRepeater.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Persistent (home-relative) path
|
||||
DelegateChoice {
|
||||
roleValue: 2
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
delegate: ColumnLayout {
|
||||
id: relD
|
||||
required property int index
|
||||
required property string value
|
||||
required property int status
|
||||
required property bool removable
|
||||
readonly property int sourceRow: catModel.mapToSource(catModel.index(index, 0)).row
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
QQC2.TextField {
|
||||
text: relD.value
|
||||
@@ -221,13 +283,19 @@ Kirigami.ScrollablePage {
|
||||
onClicked: page.controller.removeEntry(relD.sourceRow)
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: relD.index !== rowRepeater.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Environment variable
|
||||
DelegateChoice {
|
||||
roleValue: 3
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
delegate: ColumnLayout {
|
||||
id: varD
|
||||
required property int index
|
||||
required property string value
|
||||
@@ -235,8 +303,11 @@ Kirigami.ScrollablePage {
|
||||
required property int status
|
||||
required property bool removable
|
||||
readonly property int sourceRow: catModel.mapToSource(catModel.index(index, 0)).row
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
QQC2.TextField {
|
||||
text: varD.value
|
||||
@@ -262,13 +333,19 @@ Kirigami.ScrollablePage {
|
||||
onClicked: page.controller.removeEntry(varD.sourceRow)
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: varD.index !== rowRepeater.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// D-Bus name policy
|
||||
DelegateChoice {
|
||||
roleValue: 4
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
delegate: ColumnLayout {
|
||||
id: busD
|
||||
required property int index
|
||||
required property string value
|
||||
@@ -276,8 +353,11 @@ Kirigami.ScrollablePage {
|
||||
required property int status
|
||||
required property bool removable
|
||||
readonly property int sourceRow: catModel.mapToSource(catModel.index(index, 0)).row
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
QQC2.TextField {
|
||||
text: busD.value
|
||||
@@ -299,13 +379,19 @@ Kirigami.ScrollablePage {
|
||||
onClicked: page.controller.removeEntry(busD.sourceRow)
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: busD.index !== rowRepeater.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Portal (tri-state)
|
||||
DelegateChoice {
|
||||
roleValue: 5
|
||||
delegate: FormCard.FormComboBoxDelegate {
|
||||
delegate: ColumnLayout {
|
||||
id: portalD
|
||||
required property int index
|
||||
required property string label
|
||||
@@ -315,19 +401,51 @@ Kirigami.ScrollablePage {
|
||||
required property string unsupportedReason
|
||||
readonly property int sourceRow: catModel.mapToSource(catModel.index(index, 0)).row
|
||||
readonly property var states: [2, 4, 3]
|
||||
text: label
|
||||
description: supported ? example : unsupportedReason
|
||||
enabled: supported
|
||||
model: [i18n("Unset"), i18n("Allowed"), i18n("Disallowed")]
|
||||
currentIndex: value === 4 ? 1 : (value === 3 ? 2 : 0)
|
||||
onActivated: page.controller.setPortalValue(sourceRow, states[currentIndex])
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: portalD.label
|
||||
wrapMode: Text.Wrap
|
||||
enabled: portalD.supported
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: portalD.supported ? portalD.example : portalD.unsupportedReason
|
||||
visible: text.length > 0
|
||||
wrapMode: Text.Wrap
|
||||
font: Kirigami.Theme.smallFont
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
}
|
||||
QQC2.ComboBox {
|
||||
enabled: portalD.supported
|
||||
model: [i18n("Unset"), i18n("Allowed"), i18n("Disallowed")]
|
||||
currentIndex: portalD.value === 4 ? 1 : (portalD.value === 3 ? 2 : 0)
|
||||
onActivated: page.controller.setPortalValue(portalD.sourceRow, portalD.states[currentIndex])
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: portalD.index !== rowRepeater.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Add entry" affordance
|
||||
DelegateChoice {
|
||||
roleValue: 6
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
delegate: QQC2.Button {
|
||||
id: addD
|
||||
required property string categoryId
|
||||
text: i18nc("@action", "Add…")
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
// Styled after KDE System Settings' Audio page (plasma-pa's kcm/ui/main.qml):
|
||||
// Kirigami.ListSectionHeader per section, flat rows indented under the
|
||||
// header, no card/border around them.
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import io.github.toservetheking.FlatKontrol
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:window", "Preferences")
|
||||
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
// No topPadding: Kirigami.ListSectionHeader (the first thing on the
|
||||
// page) already carries its own top padding, and stacking ours on top
|
||||
// of that left an oversized gap above "Appearance".
|
||||
bottomPadding: Kirigami.Units.gridUnit
|
||||
|
||||
ColumnLayout {
|
||||
width: root.width
|
||||
spacing: 0
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
Layout.fillWidth: true
|
||||
text: i18nc("@title:group", "Appearance")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18nc("@label:listbox", "Color scheme")
|
||||
}
|
||||
QQC2.ComboBox {
|
||||
id: colorSchemeCombo
|
||||
Layout.fillWidth: true
|
||||
|
||||
model: AppColorScheme.colorSchemesModel
|
||||
textRole: "display"
|
||||
|
||||
delegate: QQC2.ItemDelegate {
|
||||
id: schemeDelegate
|
||||
required property var model
|
||||
width: colorSchemeCombo.width
|
||||
|
||||
icon.source: "image://colorScheme/" + schemeDelegate.model.display
|
||||
icon.color: "transparent"
|
||||
text: schemeDelegate.model.display
|
||||
highlighted: schemeDelegate.model.display === AppColorScheme.activeColorSchemeName
|
||||
|
||||
onClicked: {
|
||||
AppColorScheme.activeColorSchemeName = schemeDelegate.model.display;
|
||||
colorSchemeCombo.popup.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the closed-box label in sync without fighting the popup's own selection state.
|
||||
displayText: AppColorScheme.activeColorSchemeName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user