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
@@ -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. -->
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
name: Build and Publish Flatpak
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
name: Build and Test
|
||||
|
||||
|
||||
@@ -8,3 +8,5 @@
|
||||
# Editor / misc
|
||||
*.user
|
||||
*.autosave
|
||||
# Generated by ECM's KDEClangFormat module
|
||||
/.clang-format
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
@@ -1,5 +1,7 @@
|
||||
# FlatKontrol
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
@@ -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]>"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
add_executable(resolutiontest
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 526 B |
|
After Width: | Height: | Size: 739 B |
|
After Width: | Height: | Size: 971 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||