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:
2026-07-03 16:25:29 -05:00
parent a657287bd1
commit 3a17433f53
42 changed files with 590 additions and 71 deletions
+16
View File
@@ -0,0 +1,16 @@
<!--
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
SPDX-License-Identifier: CC0-1.0
-->
## Reason for the change
<!-- What problem does this solve, or what does it add? -->
## Test plan
<!-- How did you verify this? Build/run steps, commands, etc. -->
## Screenshots
<!-- If this changes UI, include a before/after. -->
+2
View File
@@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
name: Build and Publish Flatpak
+38
View File
@@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
name: Lint
on:
pull_request: {}
push:
branches:
- main
jobs:
reuse:
name: REUSE compliance
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: REUSE Compliance Check
uses: fsfe/reuse-action@v3
clang-format:
name: clang-format
runs-on: ubuntu-latest
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10
options: --privileged
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure (generates the KDE .clang-format style)
run: cmake -B build -G Ninja -DBUILD_TESTING=OFF
- name: Check formatting
run: |
find src autotests -name '*.h' -o -name '*.cpp' | \
xargs clang-format --dry-run --Werror
+2
View File
@@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
name: Build and Test
+2
View File
@@ -8,3 +8,5 @@
# Editor / misc
*.user
*.autosave
# Generated by ECM's KDEClangFormat module
/.clang-format
+22 -3
View File
@@ -1,9 +1,11 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
# FlatKontrol - a Kirigami/Qt Flatpak permissions manager
cmake_minimum_required(VERSION 3.16)
project(flatkontrol VERSION 0.2.1 LANGUAGES CXX)
project(flatkontrol VERSION 0.2.2 LANGUAGES CXX)
set(REQUIRED_QT_VERSION 6.6.0)
set(REQUIRED_KF_VERSION 6.5.0)
@@ -15,7 +17,10 @@ include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMSetupVersion)
include(ECMInstallIcons)
include(FeatureSummary)
include(KDEClangFormat)
include(KDEGitCommitHooks)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -39,6 +44,19 @@ find_package(KF6 ${REQUIRED_KF_VERSION} REQUIRED COMPONENTS
IconThemes
Crash
DBusAddons
ColorScheme
)
find_package(KF6Kirigami ${REQUIRED_KF_VERSION} REQUIRED)
set_package_properties(KF6Kirigami PROPERTIES
TYPE REQUIRED
DESCRIPTION "KDE's next-gen UI framework"
)
find_package(KF6KirigamiAddons REQUIRED)
set_package_properties(KF6KirigamiAddons PROPERTIES
TYPE REQUIRED
DESCRIPTION "Add-on components for Kirigami"
)
ecm_setup_version(${PROJECT_VERSION}
@@ -46,6 +64,7 @@ ecm_setup_version(${PROJECT_VERSION}
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/flatkontrol-version.h"
)
add_subdirectory(icons)
add_subdirectory(src)
if(BUILD_TESTING)
@@ -56,11 +75,11 @@ install(PROGRAMS io.github.toservetheking.FlatKontrol.desktop
DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES io.github.toservetheking.FlatKontrol.metainfo.xml
DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES icons/io.github.toservetheking.FlatKontrol.svg
DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps)
install(FILES LICENSES/GPL-3.0-or-later.txt
DESTINATION ${KDE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME})
# Translations: add a po/ directory and re-enable ki18n_install(po) once present.
kde_configure_git_pre_commit_hook(CHECKS CLANG-FORMAT)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
+20
View File
@@ -0,0 +1,20 @@
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 23,
"patch": 0
},
"configurePresets": [
{
"name": "ninja-dev",
"displayName": "Ninja (dev)",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"BUILD_TESTING": "ON"
}
}
]
}
+2
View File
@@ -0,0 +1,2 @@
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
SPDX-License-Identifier: CC0-1.0
+5 -1
View File
@@ -1,5 +1,7 @@
# FlatKontrol
![FlatKontrol](screenshots/main.png)
A Flatpak permissions manager built the KDE way - C++ with a QML/Kirigami
frontend, ECM/CMake, and KDE Frameworks 6. Think [Flatseal], reimagined as if
the team behind [Elisa] had written it.
@@ -51,7 +53,8 @@ toolchain. On Arch/CachyOS:
```sh
sudo pacman -S --needed cmake extra-cmake-modules base-devel \
qt6-base qt6-declarative kirigami kirigami-addons \
ki18n kcoreaddons kiconthemes kcrash kdbusaddons kitemmodels qqc2-desktop-style
ki18n kcoreaddons kiconthemes kcrash kdbusaddons kitemmodels \
kcolorscheme qqc2-desktop-style
```
Then:
@@ -70,6 +73,7 @@ cmake --build build
- `permissioncatalog.{h,cpp}` - the static catalog of categories and options.
- `portalsbackend.{h,cpp}` - QtDBus PermissionStore client.
- `permissionscontroller.{h,cpp}` - per-app state, resolution, and the row model.
- `appcolorscheme.{h,cpp}` - QML-facing wrapper around `KColorSchemeManager` for the Preferences page.
- `qml/` - Kirigami UI.
[Flatseal]: https://github.com/tchx84/Flatseal
+1
View File
@@ -8,6 +8,7 @@ path = [
"io.github.toservetheking.FlatKontrol.desktop",
"keys/*.asc",
"screenshots/*",
"icons/*.png",
]
SPDX-License-Identifier = "GPL-3.0-or-later"
SPDX-FileCopyrightText = "ToServeTheKing <[email protected]>"
+2
View File
@@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
add_executable(resolutiontest
+6 -1
View File
@@ -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 "permissionscontroller.h"
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

+16
View File
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
ecm_install_icons(ICONS
sc-apps-flatkontrol.svg
128-apps-flatkontrol.png
64-apps-flatkontrol.png
48-apps-flatkontrol.png
44-apps-flatkontrol.png
32-apps-flatkontrol.png
22-apps-flatkontrol.png
16-apps-flatkontrol.png
DESTINATION ${KDE_INSTALL_ICONDIR}
THEME hicolor
)
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]> -->
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<defs>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -61,6 +61,16 @@
<content_rating type="oars-1.1"/>
<releases>
<release version="0.2.2" date="2026-07-03">
<description>
<ul>
<li>Add a Preferences page with a color-scheme switcher</li>
<li>Add an About page</li>
<li>Ship a full hicolor icon set instead of a single scalable SVG</li>
<li>Match KDE System Settings' Audio page layout for the permission list</li>
</ul>
</description>
</release>
<release version="0.2.1" date="2026-07-03">
<description>
<ul>
+2
View File
@@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
#
# SPDX-License-Identifier: GPL-3.0-or-later
id: io.github.toservetheking.FlatKontrol
runtime: org.kde.Platform
+7
View File
@@ -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})
+34
View File
@@ -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();
}
+40
View File
@@ -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;
};
+6 -1
View File
@@ -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>
+6 -1
View File
@@ -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"
+6 -1
View File
@@ -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"
+6 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+6 -1
View File
@@ -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>
+6 -1
View File
@@ -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>
+6 -1
View File
@@ -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>
+7 -2
View File
@@ -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;
+6 -6
View File
@@ -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
+6 -6
View File
@@ -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
View File
@@ -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
}
+151 -33
View File
@@ -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,11 +109,11 @@ 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
@@ -113,7 +122,16 @@ Kirigami.ScrollablePage {
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
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: value === 4 ? 1 : (value === 3 ? 2 : 0)
onActivated: page.controller.setPortalValue(sourceRow, states[currentIndex])
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…")
+79
View File
@@ -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
}
}
}
}