Add per-tool settings and gesture customization
This commit is contained in:
@@ -16,7 +16,7 @@ include(KDECMakeSettings)
|
||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
|
||||
find_package(Qt6 6.8 REQUIRED COMPONENTS Core Quick Test)
|
||||
find_package(KF6 6.0 REQUIRED COMPONENTS Auth CoreAddons I18n KCMUtils)
|
||||
find_package(KF6 6.0 REQUIRED COMPONENTS Auth Config CoreAddons I18n KCMUtils)
|
||||
|
||||
add_subdirectory(src)
|
||||
if(BUILD_TESTING)
|
||||
|
||||
@@ -14,6 +14,11 @@ installation to a narrowly scoped KAuth helper. The helper accepts only valid
|
||||
package names that independently resolve to that capability in the
|
||||
`fedora-tools` repository.
|
||||
|
||||
Installed tools can also be configured or removed from the module. Package
|
||||
operations run one at a time; other actions remain disabled until DNF finishes.
|
||||
The module currently configures task-group shortcut behavior, touchpad hold-tap
|
||||
timing and output, and the experimental fingerprint workaround.
|
||||
|
||||
Installing this module does not install or activate any other tool. Tools
|
||||
remain usable without the module.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name: fedora-tools-settings
|
||||
Version: 0.1.0
|
||||
Release: 1%{?dist}
|
||||
Release: 5%{?dist}
|
||||
Summary: Plasma System Settings module for Fedora Tools
|
||||
|
||||
License: MIT
|
||||
@@ -14,6 +14,7 @@ BuildRequires: extra-cmake-modules
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: kf6-kauth-devel
|
||||
BuildRequires: kf6-kcmutils-devel
|
||||
BuildRequires: kf6-kconfig-devel
|
||||
BuildRequires: kf6-ki18n-devel
|
||||
BuildRequires: kf6-kirigami-devel
|
||||
BuildRequires: qt6-qtdeclarative-devel
|
||||
@@ -58,5 +59,20 @@ install -Dpm 0644 %{SOURCE2} \
|
||||
%{_datadir}/polkit-1/actions/se.ajpanton.fedoratools.policy
|
||||
|
||||
%changelog
|
||||
* Sat Sep 05 2026 fedora-tools contributors - 0.1.0-5
|
||||
- Fix button contrast and timing-value loading
|
||||
- Separate touchpad anchor age from its stationary-time setting
|
||||
|
||||
* Sat Sep 05 2026 fedora-tools contributors - 0.1.0-4
|
||||
- Improve tool descriptions and configuration help
|
||||
- Add the minimum hold-tap duration setting
|
||||
|
||||
* Sat Sep 05 2026 fedora-tools contributors - 0.1.0-3
|
||||
- Add compact deterministic tool rows and per-tool configuration
|
||||
|
||||
* Sat Sep 05 2026 fedora-tools contributors - 0.1.0-2
|
||||
- Show installation progress beside the selected tool
|
||||
- Add safe per-tool uninstall actions
|
||||
|
||||
* Sat Sep 05 2026 fedora-tools contributors - 0.1.0-1
|
||||
- Initial package
|
||||
|
||||
@@ -13,6 +13,7 @@ target_link_libraries(kcm_fedora_tools PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Quick
|
||||
KF6::AuthCore
|
||||
KF6::ConfigCore
|
||||
KF6::CoreAddons
|
||||
KF6::I18n
|
||||
KF6::KCMUtilsQuick
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
|
||||
#include <KAuth/Action>
|
||||
#include <KAuth/ExecuteJob>
|
||||
#include <KConfigGroup>
|
||||
#include <KJob>
|
||||
#include <KLocalizedString>
|
||||
#include <KPluginFactory>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QTimer>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -33,8 +37,10 @@ FedoraToolsKcm::FedoraToolsKcm(QObject *parent, const KPluginMetaData &data)
|
||||
}
|
||||
setMessage(i18n("Could not start %1.", m_query.program()), true);
|
||||
m_stage = QueryStage::Idle;
|
||||
setPackageOperation({}, {});
|
||||
setBusy(false);
|
||||
});
|
||||
loadToolSettings();
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
refresh();
|
||||
});
|
||||
@@ -65,6 +71,56 @@ bool FedoraToolsKcm::fingerprintEnabled() const
|
||||
return m_fingerprintEnabled;
|
||||
}
|
||||
|
||||
QString FedoraToolsKcm::activePackage() const
|
||||
{
|
||||
return m_activePackage;
|
||||
}
|
||||
|
||||
QString FedoraToolsKcm::packageOperation() const
|
||||
{
|
||||
return m_packageOperation;
|
||||
}
|
||||
|
||||
bool FedoraToolsKcm::shortcutStartWithFirst() const
|
||||
{
|
||||
return m_shortcutStartWithFirst;
|
||||
}
|
||||
|
||||
bool FedoraToolsKcm::shortcutInitialShiftOpensNew() const
|
||||
{
|
||||
return m_shortcutInitialShiftOpensNew;
|
||||
}
|
||||
|
||||
bool FedoraToolsKcm::shortcutShiftCyclesBackward() const
|
||||
{
|
||||
return m_shortcutShiftCyclesBackward;
|
||||
}
|
||||
|
||||
int FedoraToolsKcm::touchpadMinimumAnchorAge() const
|
||||
{
|
||||
return m_touchpadMinimumAnchorAge;
|
||||
}
|
||||
|
||||
int FedoraToolsKcm::touchpadMinimumPause() const
|
||||
{
|
||||
return m_touchpadMinimumPause;
|
||||
}
|
||||
|
||||
int FedoraToolsKcm::touchpadMinimumTap() const
|
||||
{
|
||||
return m_touchpadMinimumTap;
|
||||
}
|
||||
|
||||
int FedoraToolsKcm::touchpadMaximumTap() const
|
||||
{
|
||||
return m_touchpadMaximumTap;
|
||||
}
|
||||
|
||||
QString FedoraToolsKcm::touchpadOutputEvent() const
|
||||
{
|
||||
return m_touchpadOutputEvent;
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::refresh(bool refreshMetadata)
|
||||
{
|
||||
if (m_busy) {
|
||||
@@ -98,12 +154,107 @@ void FedoraToolsKcm::installTool(const QString &packageName)
|
||||
return;
|
||||
}
|
||||
|
||||
setPackageOperation(packageName, QStringLiteral("install"));
|
||||
runAuthorizedAction(QStringLiteral("installpackage"),
|
||||
{{QStringLiteral("packageName"), packageName}},
|
||||
i18n("%1 is now installed.", packageName),
|
||||
true);
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::removeTool(const QString &packageName)
|
||||
{
|
||||
if (m_busy || !m_tools.mayRemove(packageName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (packageName == QLatin1String("plasma-fingerprint-workaround") && m_fingerprintEnabled) {
|
||||
setMessage(i18n("Disable the fingerprint workaround before removing its controller."), true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packageName == QLatin1String("plasma-task-group-shortcuts")) {
|
||||
QProcess restore;
|
||||
restore.start(QStringLiteral("/usr/bin/plasma-task-group-shortcuts"), {QStringLiteral("--restore-shortcuts")});
|
||||
if (!restore.waitForFinished(10 * 1000) || restore.exitStatus() != QProcess::NormalExit || restore.exitCode() != 0) {
|
||||
restore.kill();
|
||||
restore.waitForFinished();
|
||||
setMessage(i18n("Could not restore the original Meta+number shortcuts."), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setPackageOperation(packageName, QStringLiteral("remove"));
|
||||
runAuthorizedAction(QStringLiteral("removepackage"),
|
||||
{{QStringLiteral("packageName"), packageName}},
|
||||
i18n("%1 was removed.", packageName),
|
||||
true);
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::saveShortcutSettings(bool startWithFirst,
|
||||
bool initialShiftOpensNew,
|
||||
bool shiftCyclesBackward)
|
||||
{
|
||||
const auto config = KSharedConfig::openConfig(QStringLiteral("plasma-task-group-shortcutsrc"));
|
||||
KConfigGroup group(config, QStringLiteral("Settings"));
|
||||
group.writeEntry("StartWithFirstWindow", startWithFirst);
|
||||
group.writeEntry("InitialShiftOpensNewInstance", initialShiftOpensNew);
|
||||
group.writeEntry("ShiftCyclesBackward", shiftCyclesBackward);
|
||||
group.sync();
|
||||
loadToolSettings();
|
||||
setMessage(i18n("Shortcut settings were saved."));
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::resetShortcutSettings()
|
||||
{
|
||||
const auto config = KSharedConfig::openConfig(QStringLiteral("plasma-task-group-shortcutsrc"));
|
||||
KConfigGroup group(config, QStringLiteral("Settings"));
|
||||
group.deleteGroup();
|
||||
group.sync();
|
||||
loadToolSettings();
|
||||
setMessage(i18n("Shortcut settings were reset to their defaults."));
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::saveTouchpadSettings(int minimumAnchorAge,
|
||||
int minimumPause,
|
||||
int minimumTap,
|
||||
int maximumTap,
|
||||
const QString &outputEvent)
|
||||
{
|
||||
static const QRegularExpression eventName(QStringLiteral("^(BTN|KEY)_[A-Z0-9_]+$"));
|
||||
if (m_busy) {
|
||||
return;
|
||||
}
|
||||
if (minimumAnchorAge < 0 || minimumAnchorAge > 2000
|
||||
|| minimumPause < 0 || minimumPause > 2000
|
||||
|| minimumTap < 0 || minimumTap > maximumTap
|
||||
|| maximumTap < 1 || maximumTap > 2000
|
||||
|| !eventName.match(outputEvent).hasMatch()) {
|
||||
setMessage(i18n("Enter valid timing values and an evdev BTN_* or KEY_* output event."), true);
|
||||
return;
|
||||
}
|
||||
|
||||
runAuthorizedAction(QStringLiteral("configuretouchpad"),
|
||||
{{QStringLiteral("operation"), QStringLiteral("save")},
|
||||
{QStringLiteral("minimumAnchorAge"), minimumAnchorAge},
|
||||
{QStringLiteral("minimumPause"), minimumPause},
|
||||
{QStringLiteral("minimumTap"), minimumTap},
|
||||
{QStringLiteral("maximumTap"), maximumTap},
|
||||
{QStringLiteral("outputEvent"), outputEvent}},
|
||||
i18n("Touchpad settings were saved. Log out and back in to apply them."),
|
||||
false);
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::resetTouchpadSettings()
|
||||
{
|
||||
if (m_busy) {
|
||||
return;
|
||||
}
|
||||
runAuthorizedAction(QStringLiteral("configuretouchpad"),
|
||||
{{QStringLiteral("operation"), QStringLiteral("reset")}},
|
||||
i18n("Touchpad settings were reset. Log out and back in to apply the defaults."),
|
||||
false);
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::enableFingerprintWorkaround(const QUrl &rpmFile, bool force)
|
||||
{
|
||||
if (m_busy) {
|
||||
@@ -155,6 +306,7 @@ void FedoraToolsKcm::queryFinished(int exitCode, QProcess::ExitStatus exitStatus
|
||||
if (exitStatus != QProcess::NormalExit) {
|
||||
setMessage(i18n("Package discovery terminated unexpectedly."), true);
|
||||
m_stage = QueryStage::Idle;
|
||||
setPackageOperation({}, {});
|
||||
setBusy(false);
|
||||
return;
|
||||
}
|
||||
@@ -183,6 +335,7 @@ void FedoraToolsKcm::queryFinished(int exitCode, QProcess::ExitStatus exitStatus
|
||||
setMessage(details.isEmpty() ? i18n("The Fedora Tools repository is unavailable.") : details, true);
|
||||
updateFingerprintStatus();
|
||||
m_stage = QueryStage::Idle;
|
||||
setPackageOperation({}, {});
|
||||
setBusy(false);
|
||||
return;
|
||||
}
|
||||
@@ -244,6 +397,7 @@ void FedoraToolsKcm::finishRefresh()
|
||||
m_tools.setPackages(m_installedPackages, m_availablePackages, m_updates);
|
||||
updateFingerprintStatus();
|
||||
m_stage = QueryStage::Idle;
|
||||
setPackageOperation({}, {});
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
@@ -274,6 +428,7 @@ void FedoraToolsKcm::actionFinished(KJob *job, const QString &successMessage, bo
|
||||
const QString helperMessage = executeJob ? executeJob->data().value(QStringLiteral("message")).toString() : QString();
|
||||
const QString errorMessage = helperMessage.isEmpty() ? job->errorString() : helperMessage;
|
||||
setMessage(errorMessage, true);
|
||||
setPackageOperation({}, {});
|
||||
setBusy(false);
|
||||
return;
|
||||
}
|
||||
@@ -283,6 +438,7 @@ void FedoraToolsKcm::actionFinished(KJob *job, const QString &successMessage, bo
|
||||
refresh();
|
||||
} else {
|
||||
updateFingerprintStatus();
|
||||
loadToolSettings();
|
||||
setBusy(false);
|
||||
}
|
||||
setMessage(successMessage);
|
||||
@@ -297,6 +453,16 @@ void FedoraToolsKcm::setBusy(bool busy)
|
||||
Q_EMIT busyChanged();
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::setPackageOperation(const QString &packageName, const QString &operation)
|
||||
{
|
||||
if (m_activePackage == packageName && m_packageOperation == operation) {
|
||||
return;
|
||||
}
|
||||
m_activePackage = packageName;
|
||||
m_packageOperation = operation;
|
||||
Q_EMIT packageOperationChanged();
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::setMessage(const QString &message, bool error)
|
||||
{
|
||||
if (m_message == message && m_error == error) {
|
||||
@@ -320,4 +486,41 @@ void FedoraToolsKcm::updateFingerprintStatus()
|
||||
}
|
||||
}
|
||||
|
||||
void FedoraToolsKcm::loadToolSettings()
|
||||
{
|
||||
const auto shortcutConfig = KSharedConfig::openConfig(QStringLiteral("plasma-task-group-shortcutsrc"));
|
||||
shortcutConfig->reparseConfiguration();
|
||||
const KConfigGroup shortcuts(shortcutConfig, QStringLiteral("Settings"));
|
||||
m_shortcutStartWithFirst = shortcuts.readEntry("StartWithFirstWindow", true);
|
||||
m_shortcutInitialShiftOpensNew = shortcuts.readEntry("InitialShiftOpensNewInstance", true);
|
||||
m_shortcutShiftCyclesBackward = shortcuts.readEntry("ShiftCyclesBackward", true);
|
||||
|
||||
m_touchpadMinimumAnchorAge = 100;
|
||||
m_touchpadMinimumPause = 100;
|
||||
m_touchpadMinimumTap = 10;
|
||||
m_touchpadMaximumTap = 150;
|
||||
m_touchpadOutputEvent = QStringLiteral("BTN_MIDDLE");
|
||||
QFile touchpadConfig(QStringLiteral("/etc/touchpad-hold-tap.conf"));
|
||||
if (touchpadConfig.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
while (!touchpadConfig.atEnd()) {
|
||||
const QList<QByteArray> setting = touchpadConfig.readLine().trimmed().split('=');
|
||||
if (setting.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
if (setting.at(0) == "MINIMUM_ANCHOR_AGE_MS") {
|
||||
m_touchpadMinimumAnchorAge = setting.at(1).toInt();
|
||||
} else if (setting.at(0) == "MINIMUM_PAUSE_MS") {
|
||||
m_touchpadMinimumPause = setting.at(1).toInt();
|
||||
} else if (setting.at(0) == "MINIMUM_TAP_MS") {
|
||||
m_touchpadMinimumTap = setting.at(1).toInt();
|
||||
} else if (setting.at(0) == "MAXIMUM_TAP_MS") {
|
||||
m_touchpadMaximumTap = setting.at(1).toInt();
|
||||
} else if (setting.at(0) == "OUTPUT_EVENT") {
|
||||
m_touchpadOutputEvent = QString::fromLatin1(setting.at(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
Q_EMIT toolSettingsChanged();
|
||||
}
|
||||
|
||||
#include "fedoratoolskcm.moc"
|
||||
|
||||
@@ -21,6 +21,16 @@ class FedoraToolsKcm : public KQuickConfigModule
|
||||
Q_PROPERTY(QString message READ message NOTIFY messageChanged)
|
||||
Q_PROPERTY(bool error READ error NOTIFY messageChanged)
|
||||
Q_PROPERTY(bool fingerprintEnabled READ fingerprintEnabled NOTIFY fingerprintEnabledChanged)
|
||||
Q_PROPERTY(QString activePackage READ activePackage NOTIFY packageOperationChanged)
|
||||
Q_PROPERTY(QString packageOperation READ packageOperation NOTIFY packageOperationChanged)
|
||||
Q_PROPERTY(bool shortcutStartWithFirst READ shortcutStartWithFirst NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(bool shortcutInitialShiftOpensNew READ shortcutInitialShiftOpensNew NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(bool shortcutShiftCyclesBackward READ shortcutShiftCyclesBackward NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(int touchpadMinimumAnchorAge READ touchpadMinimumAnchorAge NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(int touchpadMinimumPause READ touchpadMinimumPause NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(int touchpadMinimumTap READ touchpadMinimumTap NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(int touchpadMaximumTap READ touchpadMaximumTap NOTIFY toolSettingsChanged)
|
||||
Q_PROPERTY(QString touchpadOutputEvent READ touchpadOutputEvent NOTIFY toolSettingsChanged)
|
||||
|
||||
public:
|
||||
FedoraToolsKcm(QObject *parent, const KPluginMetaData &data);
|
||||
@@ -30,10 +40,25 @@ public:
|
||||
QString message() const;
|
||||
bool error() const;
|
||||
bool fingerprintEnabled() const;
|
||||
QString activePackage() const;
|
||||
QString packageOperation() const;
|
||||
bool shortcutStartWithFirst() const;
|
||||
bool shortcutInitialShiftOpensNew() const;
|
||||
bool shortcutShiftCyclesBackward() const;
|
||||
int touchpadMinimumAnchorAge() const;
|
||||
int touchpadMinimumPause() const;
|
||||
int touchpadMinimumTap() const;
|
||||
int touchpadMaximumTap() const;
|
||||
QString touchpadOutputEvent() const;
|
||||
|
||||
Q_INVOKABLE void refresh(bool refreshMetadata = false);
|
||||
Q_INVOKABLE void clearMessage();
|
||||
Q_INVOKABLE void installTool(const QString &packageName);
|
||||
Q_INVOKABLE void removeTool(const QString &packageName);
|
||||
Q_INVOKABLE void saveShortcutSettings(bool startWithFirst, bool initialShiftOpensNew, bool shiftCyclesBackward);
|
||||
Q_INVOKABLE void resetShortcutSettings();
|
||||
Q_INVOKABLE void saveTouchpadSettings(int minimumAnchorAge, int minimumPause, int minimumTap, int maximumTap, const QString &outputEvent);
|
||||
Q_INVOKABLE void resetTouchpadSettings();
|
||||
Q_INVOKABLE void enableFingerprintWorkaround(const QUrl &rpmFile, bool force);
|
||||
Q_INVOKABLE void disableFingerprintWorkaround();
|
||||
|
||||
@@ -41,6 +66,8 @@ Q_SIGNALS:
|
||||
void busyChanged();
|
||||
void messageChanged();
|
||||
void fingerprintEnabledChanged();
|
||||
void packageOperationChanged();
|
||||
void toolSettingsChanged();
|
||||
|
||||
private:
|
||||
enum class QueryStage {
|
||||
@@ -57,8 +84,10 @@ private:
|
||||
void runAuthorizedAction(const QString &name, const QVariantMap &arguments, const QString &successMessage, bool refreshTools);
|
||||
void actionFinished(KJob *job, const QString &successMessage, bool refreshTools);
|
||||
void setBusy(bool busy);
|
||||
void setPackageOperation(const QString &packageName, const QString &operation);
|
||||
void setMessage(const QString &message, bool error = false);
|
||||
void updateFingerprintStatus();
|
||||
void loadToolSettings();
|
||||
|
||||
ToolModel m_tools;
|
||||
QProcess m_query;
|
||||
@@ -70,5 +99,15 @@ private:
|
||||
bool m_busy = false;
|
||||
bool m_error = false;
|
||||
bool m_fingerprintEnabled = false;
|
||||
QString m_activePackage;
|
||||
QString m_packageOperation;
|
||||
QString m_message;
|
||||
bool m_shortcutStartWithFirst = true;
|
||||
bool m_shortcutInitialShiftOpensNew = true;
|
||||
bool m_shortcutShiftCyclesBackward = true;
|
||||
int m_touchpadMinimumAnchorAge = 100;
|
||||
int m_touchpadMinimumPause = 100;
|
||||
int m_touchpadMinimumTap = 10;
|
||||
int m_touchpadMaximumTap = 150;
|
||||
QString m_touchpadOutputEvent = QStringLiteral("BTN_MIDDLE");
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -71,6 +72,37 @@ bool isPublishedTool(const QString &packageName)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInstalledTool(const QString &packageName)
|
||||
{
|
||||
QProcess process;
|
||||
process.start(QStringLiteral("/usr/bin/rpm"),
|
||||
{QStringLiteral("-q"),
|
||||
QStringLiteral("--whatprovides"),
|
||||
QStringLiteral("fedora-tools-tool"),
|
||||
QStringLiteral("--qf"),
|
||||
QStringLiteral("%{name}\n")});
|
||||
if (!process.waitForFinished(30 * 1000) || process.exitCode() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const QByteArray &line : process.readAllStandardOutput().split('\n')) {
|
||||
if (QString::fromUtf8(line) == packageName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fingerprintWorkaroundEnabled()
|
||||
{
|
||||
QProcess process;
|
||||
process.start(QStringLiteral("/usr/bin/plasma-fingerprint-workaround"), {QStringLiteral("status")});
|
||||
return process.waitForFinished(10 * 1000)
|
||||
&& process.exitStatus() == QProcess::NormalExit
|
||||
&& process.exitCode() == 0
|
||||
&& process.readAllStandardOutput().startsWith("Fingerprint workaround: enabled");
|
||||
}
|
||||
}
|
||||
|
||||
class FedoraToolsHelper : public QObject
|
||||
@@ -94,6 +126,76 @@ public Q_SLOTS:
|
||||
packageName});
|
||||
}
|
||||
|
||||
KAuth::ActionReply removepackage(const QVariantMap &arguments)
|
||||
{
|
||||
const QString packageName = arguments.value(QStringLiteral("packageName")).toString();
|
||||
if (!isValidPackageName(packageName) || !isInstalledTool(packageName)) {
|
||||
KAuth::ActionReply reply = KAuth::ActionReply::HelperErrorReply();
|
||||
reply.addData(QStringLiteral("message"), QStringLiteral("The requested package is not an installed Fedora tool."));
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (packageName == QLatin1String("plasma-fingerprint-workaround") && fingerprintWorkaroundEnabled()) {
|
||||
KAuth::ActionReply reply = KAuth::ActionReply::HelperErrorReply();
|
||||
reply.addData(QStringLiteral("message"), QStringLiteral("Disable the fingerprint workaround before removing its controller."));
|
||||
return reply;
|
||||
}
|
||||
|
||||
return commandReply(QStringLiteral("/usr/bin/dnf5"),
|
||||
{QStringLiteral("remove"),
|
||||
QStringLiteral("--assumeyes"),
|
||||
QStringLiteral("--no-autoremove"),
|
||||
packageName});
|
||||
}
|
||||
|
||||
KAuth::ActionReply configuretouchpad(const QVariantMap &arguments)
|
||||
{
|
||||
if (!isInstalledTool(QStringLiteral("touchpad-hold-tap"))) {
|
||||
KAuth::ActionReply reply = KAuth::ActionReply::HelperErrorReply();
|
||||
reply.addData(QStringLiteral("message"), QStringLiteral("The touchpad hold-tap tool is not installed."));
|
||||
return reply;
|
||||
}
|
||||
|
||||
const QString operation = arguments.value(QStringLiteral("operation")).toString();
|
||||
const QString controller = QStringLiteral("/usr/bin/touchpad-hold-tap-config");
|
||||
if (!QFileInfo(controller).isExecutable()) {
|
||||
KAuth::ActionReply reply = KAuth::ActionReply::HelperErrorReply();
|
||||
reply.addData(QStringLiteral("message"), QStringLiteral("The touchpad configurator is not installed."));
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (operation == QLatin1String("reset")) {
|
||||
return commandReply(controller, {QStringLiteral("reset")});
|
||||
}
|
||||
if (operation != QLatin1String("save")) {
|
||||
return KAuth::ActionReply::InvalidActionReply();
|
||||
}
|
||||
|
||||
const int minimumAnchorAge = arguments.value(QStringLiteral("minimumAnchorAge")).toInt();
|
||||
const int minimumPause = arguments.value(QStringLiteral("minimumPause")).toInt();
|
||||
const int minimumTap = arguments.value(QStringLiteral("minimumTap")).toInt();
|
||||
const int maximumTap = arguments.value(QStringLiteral("maximumTap")).toInt();
|
||||
const QString outputEvent = arguments.value(QStringLiteral("outputEvent")).toString();
|
||||
static const QRegularExpression eventName(QStringLiteral("^(BTN|KEY)_[A-Z0-9_]+$"));
|
||||
if (minimumAnchorAge < 0 || minimumAnchorAge > 2000
|
||||
|| minimumPause < 0 || minimumPause > 2000
|
||||
|| minimumTap < 0 || minimumTap > maximumTap
|
||||
|| maximumTap < 1 || maximumTap > 2000
|
||||
|| !eventName.match(outputEvent).hasMatch()) {
|
||||
KAuth::ActionReply reply = KAuth::ActionReply::HelperErrorReply();
|
||||
reply.addData(QStringLiteral("message"), QStringLiteral("The requested touchpad settings are invalid."));
|
||||
return reply;
|
||||
}
|
||||
|
||||
return commandReply(controller,
|
||||
{QStringLiteral("set"),
|
||||
QStringLiteral("--minimum-anchor-age-ms"), QString::number(minimumAnchorAge),
|
||||
QStringLiteral("--minimum-pause-ms"), QString::number(minimumPause),
|
||||
QStringLiteral("--minimum-tap-ms"), QString::number(minimumTap),
|
||||
QStringLiteral("--maximum-tap-ms"), QString::number(maximumTap),
|
||||
QStringLiteral("--output-event"), outputEvent});
|
||||
}
|
||||
|
||||
KAuth::ActionReply fingerprintworkaround(const QVariantMap &arguments)
|
||||
{
|
||||
const QString operation = arguments.value(QStringLiteral("operation")).toString();
|
||||
|
||||
@@ -7,6 +7,16 @@ Name=Install a Fedora tool
|
||||
Description=Install a package from the Fedora Tools repository
|
||||
Policy=auth_admin
|
||||
|
||||
[se.ajpanton.fedoratools.removepackage]
|
||||
Name=Remove a Fedora tool
|
||||
Description=Remove an installed Fedora Tools package
|
||||
Policy=auth_admin
|
||||
|
||||
[se.ajpanton.fedoratools.configuretouchpad]
|
||||
Name=Configure the touchpad hold-tap gesture
|
||||
Description=Change system-wide settings for the touchpad hold-tap plugin
|
||||
Policy=auth_admin
|
||||
|
||||
[se.ajpanton.fedoratools.fingerprintworkaround]
|
||||
Name=Change the Plasma fingerprint workaround
|
||||
Description=Install or remove the experimental patched KScreenLocker package
|
||||
|
||||
@@ -42,7 +42,10 @@ QVariant ToolModel::data(const QModelIndex &index, int role) const
|
||||
case UpdateAvailableRole:
|
||||
return tool.updateAvailable;
|
||||
case ConfigurableRole:
|
||||
return tool.installed && tool.packageName == QStringLiteral("plasma-fingerprint-workaround");
|
||||
return tool.installed
|
||||
&& (tool.packageName == QStringLiteral("plasma-fingerprint-workaround")
|
||||
|| tool.packageName == QStringLiteral("plasma-task-group-shortcuts")
|
||||
|| tool.packageName == QStringLiteral("touchpad-hold-tap"));
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
@@ -83,16 +86,15 @@ void ToolModel::setPackages(const QList<PackageRecord> &installed,
|
||||
tool.packageName = package.name;
|
||||
tool.availableVersion = package.version;
|
||||
tool.architecture = package.architecture;
|
||||
tool.summary = package.summary;
|
||||
if (!tool.installed) {
|
||||
tool.summary = package.summary;
|
||||
}
|
||||
tool.available = true;
|
||||
tool.updateAvailable = tool.installed && updates.contains(package.name);
|
||||
}
|
||||
|
||||
QList<Tool> merged = tools.values();
|
||||
std::ranges::sort(merged, [](const Tool &left, const Tool &right) {
|
||||
if (left.installed != right.installed) {
|
||||
return left.installed > right.installed;
|
||||
}
|
||||
return left.packageName < right.packageName;
|
||||
});
|
||||
|
||||
@@ -107,3 +109,10 @@ bool ToolModel::mayInstall(const QString &packageName) const
|
||||
return tool.packageName == packageName && tool.available;
|
||||
});
|
||||
}
|
||||
|
||||
bool ToolModel::mayRemove(const QString &packageName) const
|
||||
{
|
||||
return std::ranges::any_of(m_tools, [&packageName](const Tool &tool) {
|
||||
return tool.packageName == packageName && tool.installed;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
const QList<PackageRecord> &available,
|
||||
const QSet<QString> &updates = {});
|
||||
bool mayInstall(const QString &packageName) const;
|
||||
bool mayRemove(const QString &packageName) const;
|
||||
|
||||
private:
|
||||
QList<Tool> m_tools;
|
||||
|
||||
@@ -12,14 +12,25 @@ import org.kde.kirigami as Kirigami
|
||||
KCM.SimpleKCM {
|
||||
id: root
|
||||
|
||||
property bool showingFingerprintSettings: false
|
||||
property string settingsPage: ""
|
||||
|
||||
implicitWidth: Kirigami.Units.gridUnit * 36
|
||||
implicitHeight: Kirigami.Units.gridUnit * 30
|
||||
|
||||
StackLayout {
|
||||
anchors.fill: parent
|
||||
currentIndex: root.showingFingerprintSettings ? 1 : 0
|
||||
currentIndex: {
|
||||
if (root.settingsPage === "plasma-fingerprint-workaround") {
|
||||
return 1
|
||||
}
|
||||
if (root.settingsPage === "plasma-task-group-shortcuts") {
|
||||
return 2
|
||||
}
|
||||
if (root.settingsPage === "touchpad-hold-tap") {
|
||||
return 3
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
@@ -50,7 +61,7 @@ KCM.SimpleKCM {
|
||||
|
||||
Controls.BusyIndicator {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: kcm.busy
|
||||
visible: kcm.busy && kcm.activePackage.length === 0
|
||||
running: visible
|
||||
}
|
||||
|
||||
@@ -83,6 +94,7 @@ KCM.SimpleKCM {
|
||||
|
||||
Kirigami.Icon {
|
||||
source: "applications-system"
|
||||
opacity: toolCard.installed ? 1 : 0
|
||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||
implicitHeight: width
|
||||
}
|
||||
@@ -124,14 +136,96 @@ KCM.SimpleKCM {
|
||||
visible: toolCard.configurable
|
||||
text: i18n("Configure…")
|
||||
enabled: !kcm.busy
|
||||
onClicked: root.showingFingerprintSettings = true
|
||||
onClicked: {
|
||||
kcm.clearMessage()
|
||||
root.settingsPage = toolCard.packageName
|
||||
if (root.settingsPage === "plasma-task-group-shortcuts") {
|
||||
shortcutPage.loadSettings()
|
||||
} else if (root.settingsPage === "touchpad-hold-tap") {
|
||||
touchpadPage.loadSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Button {
|
||||
StackLayout {
|
||||
id: installControl
|
||||
|
||||
visible: toolCard.available && (!toolCard.installed || toolCard.updateAvailable)
|
||||
text: toolCard.installed ? i18n("Update") : i18n("Install")
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.installTool(toolCard.packageName)
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 5
|
||||
Layout.preferredWidth: Layout.minimumWidth
|
||||
Layout.maximumWidth: Layout.minimumWidth
|
||||
Layout.preferredHeight: installButton.implicitHeight
|
||||
currentIndex: kcm.activePackage === toolCard.packageName
|
||||
&& kcm.packageOperation === "install" ? 1 : 0
|
||||
|
||||
Controls.Button {
|
||||
id: installButton
|
||||
|
||||
text: toolCard.installed ? i18n("Update") : i18n("Install")
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.installTool(toolCard.packageName)
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: installButton.implicitWidth
|
||||
implicitHeight: installButton.implicitHeight
|
||||
|
||||
Controls.BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.smallMedium
|
||||
height: width
|
||||
running: installControl.currentIndex === 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: removeControl
|
||||
|
||||
visible: toolCard.installed
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 5
|
||||
Layout.preferredWidth: Layout.minimumWidth
|
||||
Layout.maximumWidth: Layout.minimumWidth
|
||||
Layout.preferredHeight: removeButton.implicitHeight
|
||||
currentIndex: kcm.activePackage === toolCard.packageName
|
||||
&& kcm.packageOperation === "remove" ? 1 : 0
|
||||
|
||||
Controls.Button {
|
||||
id: removeButton
|
||||
|
||||
text: i18n("Uninstall")
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.removeTool(toolCard.packageName)
|
||||
|
||||
contentItem: Controls.Label {
|
||||
text: removeButton.text
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: Qt.tint(
|
||||
Kirigami.Theme.backgroundColor,
|
||||
Qt.alpha(Kirigami.Theme.negativeTextColor,
|
||||
removeButton.down ? 0.45 : removeButton.hovered ? 0.35 : 0.25))
|
||||
border.color: Kirigami.Theme.negativeTextColor
|
||||
opacity: removeButton.enabled ? 1 : 0.5
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: removeButton.implicitWidth
|
||||
implicitHeight: removeButton.implicitHeight
|
||||
|
||||
Controls.BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.smallMedium
|
||||
height: width
|
||||
running: removeControl.currentIndex === 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +260,7 @@ KCM.SimpleKCM {
|
||||
enabled: !kcm.busy
|
||||
onClicked: {
|
||||
kcm.clearMessage()
|
||||
root.showingFingerprintSettings = false
|
||||
root.settingsPage = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,6 +350,323 @@ KCM.SimpleKCM {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: shortcutPage
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
function loadSettings() {
|
||||
cycleStart.currentIndex = kcm.shortcutStartWithFirst ? 0 : 1
|
||||
initialShift.checked = kcm.shortcutInitialShiftOpensNew
|
||||
reverseShift.checked = kcm.shortcutShiftCyclesBackward
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Controls.ToolButton {
|
||||
text: i18n("Back to tools")
|
||||
icon.name: "go-previous"
|
||||
enabled: !kcm.busy
|
||||
onClicked: {
|
||||
kcm.clearMessage()
|
||||
root.settingsPage = ""
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Task-group shortcuts")
|
||||
level: 2
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
visible: kcm.message.length > 0
|
||||
text: kcm.message
|
||||
type: kcm.error ? Kirigami.MessageType.Error : Kirigami.MessageType.Positive
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Plasma normally assigns Meta+number shortcuts to individual Task Manager entries, so every ungrouped window consumes a number. This tool instead assigns each number to an application group in Task Manager order.\n\nPress Meta+number to select a group, then keep Meta held and press the same number again to cycle through that application's windows. Releasing Meta ends the cycle.")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Controls.ComboBox {
|
||||
id: cycleStart
|
||||
|
||||
Kirigami.FormData.label: i18n("New cycle starts with:")
|
||||
model: [i18n("First window"), i18n("Window after the active one")]
|
||||
}
|
||||
|
||||
Controls.CheckBox {
|
||||
id: initialShift
|
||||
|
||||
Kirigami.FormData.label: i18n("Shift on first press:")
|
||||
text: i18n("Open a new window")
|
||||
}
|
||||
|
||||
Controls.CheckBox {
|
||||
id: reverseShift
|
||||
|
||||
Kirigami.FormData.label: i18n("Shift while cycling:")
|
||||
text: i18n("Cycle backward")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
visible: initialShift.checked && reverseShift.checked
|
||||
type: Kirigami.MessageType.Information
|
||||
text: i18n("With both “Open a new window” and “Cycle backward” enabled, start a backward cycle with Meta+number without Shift. Keep Meta held and use Meta+Shift+number for the following backward steps.")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Controls.Button {
|
||||
text: i18n("Restore Defaults")
|
||||
icon.name: "edit-undo"
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.resetShortcutSettings()
|
||||
}
|
||||
|
||||
Controls.Button {
|
||||
text: i18n("Save")
|
||||
icon.name: "document-save"
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.saveShortcutSettings(
|
||||
cycleStart.currentIndex === 0,
|
||||
initialShift.checked,
|
||||
reverseShift.checked)
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Changes apply the next time a Meta+number shortcut is pressed.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: kcm
|
||||
function onToolSettingsChanged() {
|
||||
shortcutPage.loadSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: touchpadPage
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
function loadSettings() {
|
||||
minimumAnchorAge.value = kcm.touchpadMinimumAnchorAge
|
||||
minimumPause.value = kcm.touchpadMinimumPause
|
||||
maximumTap.value = kcm.touchpadMaximumTap
|
||||
minimumTap.value = kcm.touchpadMinimumTap
|
||||
outputEvent.editText = kcm.touchpadOutputEvent
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Controls.ToolButton {
|
||||
text: i18n("Back to tools")
|
||||
icon.name: "go-previous"
|
||||
enabled: !kcm.busy
|
||||
onClicked: {
|
||||
kcm.clearMessage()
|
||||
root.settingsPage = ""
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Touchpad hold-tap")
|
||||
level: 2
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
visible: kcm.message.length > 0
|
||||
text: kcm.message
|
||||
type: kcm.error ? Kirigami.MessageType.Error : Kirigami.MessageType.Positive
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Hold one anchor finger on the touchpad, then briefly tap with a second finger.\nMinimum anchor age is how long the first finger must have been touching the touchpad, separating this gesture from an ordinary two-finger tap.\nRequired stationary time is how long the anchor must remain within the movement tolerance before the tap; set it to 0 ms to allow hold-taps while the anchor is moving.\nMinimum tap duration rejects implausibly brief contacts, such as hardware noise that could otherwise produce unintended or duplicate actions.\nMaximum tap duration rejects a second finger that was held rather than tapped.\nOutput event selects the mouse button or single keyboard key emitted for a successful gesture.")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Controls.SpinBox {
|
||||
id: minimumAnchorAge
|
||||
|
||||
Kirigami.FormData.label: i18n("Minimum anchor age:")
|
||||
from: 0
|
||||
to: 2000
|
||||
editable: true
|
||||
textFromValue: function(value, locale) {
|
||||
return i18n("%1 ms", value)
|
||||
}
|
||||
valueFromText: function(text, locale) {
|
||||
return parseInt(text)
|
||||
}
|
||||
}
|
||||
|
||||
Controls.SpinBox {
|
||||
id: minimumPause
|
||||
|
||||
Kirigami.FormData.label: i18n("Required stationary time:")
|
||||
from: 0
|
||||
to: 2000
|
||||
editable: true
|
||||
textFromValue: function(value, locale) {
|
||||
return i18n("%1 ms", value)
|
||||
}
|
||||
valueFromText: function(text, locale) {
|
||||
return parseInt(text)
|
||||
}
|
||||
}
|
||||
|
||||
Controls.SpinBox {
|
||||
id: minimumTap
|
||||
|
||||
Kirigami.FormData.label: i18n("Minimum tap duration:")
|
||||
from: 0
|
||||
to: maximumTap.value
|
||||
editable: true
|
||||
textFromValue: function(value, locale) {
|
||||
return i18n("%1 ms", value)
|
||||
}
|
||||
valueFromText: function(text, locale) {
|
||||
return parseInt(text)
|
||||
}
|
||||
}
|
||||
|
||||
Controls.SpinBox {
|
||||
id: maximumTap
|
||||
|
||||
Kirigami.FormData.label: i18n("Maximum tap duration:")
|
||||
from: 1
|
||||
to: 2000
|
||||
editable: true
|
||||
textFromValue: function(value, locale) {
|
||||
return i18n("%1 ms", value)
|
||||
}
|
||||
valueFromText: function(text, locale) {
|
||||
return parseInt(text)
|
||||
}
|
||||
}
|
||||
|
||||
Controls.ComboBox {
|
||||
id: outputEvent
|
||||
|
||||
Kirigami.FormData.label: i18n("Output event:")
|
||||
editable: true
|
||||
model: [
|
||||
"BTN_MIDDLE",
|
||||
"BTN_LEFT",
|
||||
"BTN_RIGHT",
|
||||
"BTN_SIDE",
|
||||
"BTN_EXTRA",
|
||||
"KEY_ENTER",
|
||||
"KEY_ESC",
|
||||
"KEY_SPACE",
|
||||
"KEY_F13",
|
||||
"KEY_F14",
|
||||
"KEY_F15",
|
||||
"KEY_F16"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("The output is one Linux evdev BTN_* or KEY_* event. The default BTN_MIDDLE produces a middle click.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: kcm.busy
|
||||
|
||||
Controls.BusyIndicator {
|
||||
running: parent.visible
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: i18n("Applying change…")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Controls.Button {
|
||||
text: i18n("Restore Defaults")
|
||||
icon.name: "edit-undo"
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.resetTouchpadSettings()
|
||||
}
|
||||
|
||||
Controls.Button {
|
||||
text: i18n("Save")
|
||||
icon.name: "document-save"
|
||||
enabled: !kcm.busy
|
||||
onClicked: kcm.saveTouchpadSettings(
|
||||
minimumAnchorAge.value,
|
||||
minimumPause.value,
|
||||
minimumTap.value,
|
||||
maximumTap.value,
|
||||
outputEvent.editText.trim().toUpperCase())
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Log out and back in after saving or restoring defaults so KWin reloads the libinput plugin.")
|
||||
wrapMode: Text.WordWrap
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: kcm
|
||||
function onToolSettingsChanged() {
|
||||
touchpadPage.loadSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dialogs.FileDialog {
|
||||
|
||||
@@ -23,6 +23,8 @@ private Q_SLOTS:
|
||||
QCOMPARE(model.data(index, ToolModel::AvailableRole).toBool(), true);
|
||||
QCOMPARE(model.data(index, ToolModel::UpdateAvailableRole).toBool(), true);
|
||||
QCOMPARE(model.mayInstall(QStringLiteral("touchpad-hold-tap")), true);
|
||||
QCOMPARE(model.mayRemove(QStringLiteral("touchpad-hold-tap")), true);
|
||||
QCOMPARE(model.mayRemove(QStringLiteral("missing-tool")), false);
|
||||
}
|
||||
|
||||
void doesNotGuessUpdatesFromDifferentVersionStrings()
|
||||
@@ -44,6 +46,34 @@ private Q_SLOTS:
|
||||
|
||||
QCOMPARE(model.data(model.index(0), ToolModel::ConfigurableRole).toBool(), true);
|
||||
}
|
||||
|
||||
void sortsAlphabeticallyRegardlessOfInstallState()
|
||||
{
|
||||
ToolModel model;
|
||||
const PackageRecord installed{QStringLiteral("touchpad-hold-tap"), QStringLiteral("1"), QStringLiteral("noarch"), QStringLiteral("Touchpad")};
|
||||
const PackageRecord availableFirst{QStringLiteral("plasma-always-show-unlock"), QStringLiteral("1"), QStringLiteral("noarch"), QStringLiteral("Lock screen")};
|
||||
const PackageRecord availableLast{QStringLiteral("touchpad-hold-tap"), QStringLiteral("1"), QStringLiteral("noarch"), QStringLiteral("Old touchpad summary")};
|
||||
|
||||
model.setPackages({installed}, {availableFirst, availableLast});
|
||||
|
||||
QCOMPARE(model.data(model.index(0), ToolModel::PackageNameRole).toString(),
|
||||
QStringLiteral("plasma-always-show-unlock"));
|
||||
QCOMPARE(model.data(model.index(1), ToolModel::PackageNameRole).toString(),
|
||||
QStringLiteral("touchpad-hold-tap"));
|
||||
QCOMPARE(model.data(model.index(1), ToolModel::SummaryRole).toString(),
|
||||
QStringLiteral("Touchpad"));
|
||||
}
|
||||
|
||||
void makesInstalledToolsWithSettingsConfigurable()
|
||||
{
|
||||
ToolModel model;
|
||||
const PackageRecord shortcuts{QStringLiteral("plasma-task-group-shortcuts"), QStringLiteral("1"), QStringLiteral("x86_64"), QStringLiteral("Shortcuts")};
|
||||
const PackageRecord touchpad{QStringLiteral("touchpad-hold-tap"), QStringLiteral("1"), QStringLiteral("noarch"), QStringLiteral("Touchpad")};
|
||||
model.setPackages({shortcuts, touchpad}, {shortcuts, touchpad});
|
||||
|
||||
QCOMPARE(model.data(model.index(0), ToolModel::ConfigurableRole).toBool(), true);
|
||||
QCOMPARE(model.data(model.index(1), ToolModel::ConfigurableRole).toBool(), true);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(ToolModelTest)
|
||||
|
||||
Reference in New Issue
Block a user