kcms/powermanagement: Move to powerdevil

Issue: https://invent.kde.org/plasma/plasma-mobile/-/issues/449

Companion MR: https://invent.kde.org/plasma/powerdevil/-/merge_requests/532

Move this KCM to powerdevil to share code.
This commit is contained in:
Devin Lin 2025-03-04 19:58:02 -05:00 committed by Luigi Toscano
parent 8a03352236
commit 8d9ed17022
14 changed files with 0 additions and 1394 deletions

View file

@ -1,11 +0,0 @@
Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -3,6 +3,5 @@
add_subdirectory(mobileshell)
add_subdirectory(info)
add_subdirectory(powermanagement)
add_subdirectory(time)
add_subdirectory(virtualkeyboard)

View file

@ -1,22 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org>
add_definitions(-DTRANSLATION_DOMAIN=\"kcm_mobile_power\")
kcmutils_add_qml_kcm(kcm_mobile_power SOURCES ${kcm_mobile_power_SRCS})
target_sources(kcm_mobile_power PRIVATE
mobilepower.cpp
batterymodel.cpp
statisticsprovider.cpp
)
target_link_libraries(kcm_mobile_power PRIVATE
Qt::DBus
Qt::Core
KF6::CoreAddons
KF6::I18n
KF6::KCMUtilsQuick
KF6::ConfigCore
KF6::Solid
)

View file

@ -1,3 +0,0 @@
# SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
$XGETTEXT $(find . -name \*.cpp -o -name \*.h -o -name \*.qml) -o $podir/kcm_mobile_power.pot

View file

@ -1,91 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
* SPD
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "batterymodel.h"
#include <Solid/DeviceNotifier>
#include <QQmlEngine>
BatteryModel::BatteryModel(QObject *parent)
: QAbstractListModel(parent)
{
qmlRegisterUncreatableType<Solid::Battery>("org.kde.kinfocenter.energy.private", 1, 0, "Battery", QStringLiteral("Use Solid::Battery"));
m_batteries = Solid::Device::listFromType(Solid::DeviceInterface::Battery);
connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, this, [this](const QString &udi) {
auto it = std::find_if(m_batteries.constBegin(), m_batteries.constEnd(), [&udi](const Solid::Device &dev) {
return dev.udi() == udi;
});
if (it != m_batteries.constEnd()) {
return;
}
const Solid::Device device(udi);
if (device.isValid() && device.is<Solid::Battery>()) {
beginInsertRows(QModelIndex(), m_batteries.count(), m_batteries.count());
m_batteries.append(device);
endInsertRows();
Q_EMIT countChanged();
}
});
connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, this, [this](const QString &udi) {
auto it = std::find_if(m_batteries.constBegin(), m_batteries.constEnd(), [&udi](const Solid::Device &dev) {
return dev.udi() == udi;
});
if (it == m_batteries.constEnd()) {
return;
}
int index = std::distance(m_batteries.constBegin(), it);
beginRemoveRows(QModelIndex(), index, index);
m_batteries.removeAt(index);
endRemoveRows();
Q_EMIT countChanged();
});
}
QVariant BatteryModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_batteries.count()) {
return QVariant();
}
if (role == BatteryRole) {
// .as returns a pointer to a casted DeviceInterface. This pointer must
// not, under any circumstances, be deleted outside Solid!
// https://bugs.kde.org/show_bug.cgi?id=413003
const auto battery = m_batteries.value(index.row()).as<Solid::Battery>();
QQmlEngine::setObjectOwnership(battery, QQmlEngine::CppOwnership);
return QVariant::fromValue(battery);
} else if (role == ProductRole) {
const Solid::Device device = m_batteries.value(index.row());
return device.product();
} else if (role == VendorRole) {
const Solid::Device device = m_batteries.value(index.row());
return device.vendor();
} else if (role == UdiRole) {
return m_batteries.at(index.row()).udi();
}
return QVariant();
}
int BatteryModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_batteries.count();
}
QHash<int, QByteArray> BatteryModel::roleNames() const
{
return {{BatteryRole, "battery"}, {VendorRole, "vendor"}, {ProductRole, "product"}, {UdiRole, "udi"}};
}

View file

@ -1,42 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QAbstractListModel>
#include <QList>
#include <Solid/Battery>
#include <Solid/Device>
class BatteryModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public:
explicit BatteryModel(QObject *parent);
~BatteryModel() override = default;
enum Roles {
BatteryRole = Qt::UserRole,
UdiRole,
VendorRole,
ProductRole,
};
Q_ENUM(Roles)
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void countChanged();
private:
QList<Solid::Device> m_batteries;
};

View file

@ -1,114 +0,0 @@
{
"Categories": "Qt;KDE;X-KDE-settings-system;",
"KPlugin": {
"Description": "Configure power management settings",
"Description[ca@valencia]": "Configureu els paràmetres de gestió de l'energia",
"Description[ca]": "Configuració dels paràmetres de gestió d'energia",
"Description[cs]": "Konfigurace nastavení správy napájení",
"Description[de]": "Energieverwaltung einrichten",
"Description[en_GB]": "Configure power management settings",
"Description[eo]": "Meti agordojn pri potenc-administrado",
"Description[es]": "Configurar las preferencias de la gestión de energía",
"Description[eu]": "Konfiguratu energia kudeatzeko ezarpenak",
"Description[fi]": "Aseta virranhallinta",
"Description[fr]": "Configurer les paramètres de gestion de l'énergie",
"Description[gl]": "Configurar a xestión de enerxía.",
"Description[he]": "הגדרת ניהול החשמל",
"Description[hu]": "Energiakezeléso beállítások módosítása",
"Description[ia]": "Configura preferentias de gestion de energia",
"Description[is]": "Grunnstilla orkunotkun",
"Description[it]": "Configura le impostazioni di gestione energetica",
"Description[ka]": "ენერგიის მართვის პარამეტრების კონფიგურაცია",
"Description[ko]": "전원 관리 설정",
"Description[lv]": "Konfigurēt jaudas pārvaldības iestatījumus",
"Description[nl]": "Energie-instellingen configureren",
"Description[nn]": "Set opp straumstyring",
"Description[pa]": "ਊਰਜਾ ਇੰਤਜ਼ਾਮ ਸੈਟਿੰਗਾਂ ਦੀ ਸੰਰਚਨਾ",
"Description[pl]": "Ustawienia zarządzania energią",
"Description[pt_BR]": "Configurar as opções do gerenciamento de energia",
"Description[ru]": "Настройка параметров управления питанием",
"Description[sa]": "शक्तिप्रबन्धनसेटिंग्स् विन्यस्यताम्",
"Description[sk]": "Konfigurácia nastavení správy napájania",
"Description[sl]": "Nastavi upravljanje z energijo",
"Description[sv]": "Anpassa strömhanteringsinställningar",
"Description[ta]": "ஆற்றல் மேலாண்மையை அமைக்கலாம்",
"Description[tr]": "Güç yönetimi ayarlarını yapılandır",
"Description[uk]": "Налаштовування керування живленням",
"Description[x-test]": "xxConfigure power management settingsxx",
"Description[zh_CN]": "配置电源管理设置。",
"Description[zh_TW]": "設定電源管理",
"FormFactors": [
"handset",
"tablet",
"mediacenter"
],
"Icon": "preferences-system-power-management",
"Name": "Energy",
"Name[ar]": "الطاقة",
"Name[ca@valencia]": "Energia",
"Name[ca]": "Energia",
"Name[cs]": "Energie",
"Name[de]": "Energie",
"Name[en_GB]": "Energy",
"Name[eo]": "Energio",
"Name[es]": "Energía",
"Name[eu]": "Energia",
"Name[fi]": "Virta",
"Name[fr]": "Énergie",
"Name[gl]": "Enerxía",
"Name[he]": "חשמל",
"Name[hu]": "Energia",
"Name[ia]": "Energia",
"Name[is]": "Orka",
"Name[it]": "Energia",
"Name[ka]": "ენერგია",
"Name[ko]": "에너지",
"Name[lv]": "Enerģija",
"Name[nl]": "Energie",
"Name[nn]": "Straumstyring",
"Name[pa]": "ਊਰਜਾ",
"Name[pl]": "Energia",
"Name[pt_BR]": "Energia",
"Name[ru]": "Питание",
"Name[sa]": "ऊर्जा",
"Name[sk]": "Energia",
"Name[sl]": "Energija",
"Name[sv]": "Energi",
"Name[ta]": "ஆற்றல்",
"Name[tr]": "Enerji",
"Name[uk]": "Живлення",
"Name[x-test]": "xxEnergyxx",
"Name[zh_CN]": "电量",
"Name[zh_TW]": "電源"
},
"X-KDE-Keywords": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver",
"X-KDE-Keywords[ca@valencia]": "vídeo,monitor,gràfics,temps d'espera,dormir,bloqueig,bloqueig de pantalla,estil de pantalla",
"X-KDE-Keywords[ca]": "vídeo,monitor,gràfics,temps d'espera,dormir,bloqueig,bloqueig de pantalla,estil de pantalla",
"X-KDE-Keywords[en_GB]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver",
"X-KDE-Keywords[es]": "vídeo,monitor,gráficos,tiempo de espera,reposo,bloqueo,bloqueo de la pantalla,salvapantallas",
"X-KDE-Keywords[eu]": "bideoa, monitorea, grafikoak, denbora-muga, lo, lokartu, giltzatu, pantaila-giltzatzailea, pantaila-babeslea",
"X-KDE-Keywords[fi]": "video,näyttö,monitori,grafiikka,aikakatkaisu,lepotila,lukitus,näyttölukko,näytönsäästäjä",
"X-KDE-Keywords[fr]": "vidéo, moniteur, graphique, délai d'attente, veille, verrouillage, bloqueur d'écran, économiseur d'écran",
"X-KDE-Keywords[gl]": "vídeo, monitor, gráficos, tempo límite, hibernar, bloquear, bloqueador de pantalla, salvapantallas",
"X-KDE-Keywords[he]": "וידאו,צג,תצוגה,מסך,מקרן,גרפיקה,תום זמן,המתנה,השהיה,שינה,נעילה,נועל מסך,שומר מסך",
"X-KDE-Keywords[hu]": "videó, monitor, grafika, időkorlát, alvó állapot, zárolás, képernyőzároló, képernyővédő",
"X-KDE-Keywords[ia]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver",
"X-KDE-Keywords[it]": "video,monitor,grafica,timeout,sospensione,blocco,blocco dello schermo,salvaschermo",
"X-KDE-Keywords[ka]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver, ეკრანის შემნახველი, ვიდეო, ეკრანი, გრაფიკა, მოლოდინის დრო, ძილი",
"X-KDE-Keywords[ko]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver, 비디오, 모니터, 그래픽, 시간 제한, 절전, 대기, 잠금, 화면 보호기, 화면 잠금",
"X-KDE-Keywords[lv]": "video, monitors, grafika, noildze, iesnaudināšana, slēgšana, ekrāna bloķēšana, bloķēt, ekrāna saudzētājs",
"X-KDE-Keywords[nl]": "video, monitor, grafische elementen, timeout, slapen, vergrendeling, schermvergrendeling, schermbeveiliging",
"X-KDE-Keywords[nn]": "video, skjerm, grafikk, tidsavbrot, timeout, sove, lås, låsing, skjermlåsar, pauseskjerm, skjermsparar",
"X-KDE-Keywords[pl]": "video, monitor, grafika, timeout, uśpij, zablokuj, blokada ekranu, wygaszacz ekranu",
"X-KDE-Keywords[ru]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver, видео, монитор, графика, тайм-аут, сон, блокировка, блокировщик экрана, хранитель экрана",
"X-KDE-Keywords[sa]": "विडियो, मॉनिटर, ग्राफिक्स्, समयसमाप्तिः, निद्रा, ताला, स्क्रीनलॉकरः, स्क्रीनसेवरः",
"X-KDE-Keywords[sl]": "video, monitor, grafika, časovna omejitev, spanje, zaklepanje, zaklepanje zaslona, ohranjevalnik zaslona",
"X-KDE-Keywords[sv]": "video, bildskärm, grafik, tidsgräns, viloläge, lår, skärmlåsning, skärmsläckare",
"X-KDE-Keywords[tr]": "video,monitör,ekran,görüntü,grafik,zaman aşımı,uyku,kilit,ekran kilidi,ekran koruyucusu",
"X-KDE-Keywords[uk]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver, відео, монітор, графіка, час очікування, присипляння, блокування, збереження, екран",
"X-KDE-Keywords[x-test]": "xxvideoxx,xx monitorxx,xx graphicsxx,xx timeoutxx,xx sleepxx,xx lockxx,xx screenlockerxx,xx screensaverxx",
"X-KDE-Keywords[zh_CN]": "video, monitor, graphics, timeout, sleep, lock, screenlocker, screensaver, shipin, xianshiqi, jianshiqi, tuxing, tuxiang, chaoshi, shuimian, suoping, pingmubaohu, pingmusuoding, pingbao, suoding, 视频, 显示器, 监视器, 图形, 图像, 超时, 睡眠, 锁屏, 屏幕保护, 屏幕锁定, 屏保, 锁定",
"X-KDE-Keywords[zh_TW]": "影片,螢幕,圖形,逾時,睡眠,鎖定,螢幕鎖定,螢幕保護",
"X-KDE-System-Settings-Parent-Category": "system-administration",
"X-KDE-Weight": 20
}

View file

@ -1,232 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org>
#include "mobilepower.h"
#include "statisticsprovider.h"
#include <KConfigGroup>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KSharedConfig>
#include <Solid/Battery>
#include <QDBusConnection>
#include <QDBusPendingCall>
K_PLUGIN_CLASS_WITH_JSON(MobilePower, "kcm_mobile_power.json")
enum {
THIRTY_SECONDS,
ONE_MINUTE,
TWO_MINUTES,
FIVE_MINUTES,
TEN_MINUTES,
FIFTEEN_MINUTES,
THIRTY_MINUTES,
NEVER,
};
const QStringList timeValues = {
i18n("30 sec"),
i18n("1 min"),
i18n("2 min"),
i18n("5 min"),
i18n("10 min"),
i18n("15 min"),
i18n("30 min"),
i18n("Never"),
};
// Maps the indices of the timeValues indexes
// to seconds.
const QMap<int, qreal> idxToSeconds = {
{THIRTY_SECONDS, 30},
{ONE_MINUTE, 60},
{TWO_MINUTES, 120},
{FIVE_MINUTES, 300},
{TEN_MINUTES, 600},
{FIFTEEN_MINUTES, 900},
{THIRTY_MINUTES, 1800},
{NEVER, 0},
};
MobilePower::MobilePower(QObject *parent, const KPluginMetaData &metaData)
: KQuickConfigModule(parent, metaData)
, m_batteries{new BatteryModel(this)}
, m_profilesConfig{KSharedConfig::openConfig("powerdevilrc", KConfig::SimpleConfig | KConfig::CascadeConfig)}
{
qmlRegisterUncreatableType<BatteryModel>("org.kde.kcm.power.mobile.private", 1, 0, "BatteryModel", QStringLiteral("Use BatteryModel"));
qmlRegisterUncreatableType<Solid::Battery>("org.kde.kcm.power.mobile.private", 1, 0, "Battery", "");
qmlRegisterType<StatisticsProvider>("org.kde.kcm.power.mobile.private", 1, 0, "HistoryModel");
setButtons(KQuickConfigModule::NoAdditionalButton);
load();
}
void MobilePower::load()
{
// we assume that the [AC], [Battery], and [LowBattery] groups have the same value
// (which is done by this kcm)
KConfigGroup batteryGroup = m_profilesConfig->group("Battery");
if (batteryGroup.hasGroup("Display")) {
qDebug() << "[Battery][Display] group is listed";
KConfigGroup displaySettings = batteryGroup.group("Display");
m_dimScreenTime = displaySettings.readEntry("DimDisplayIdleTimeoutSec", 30);
m_dimScreen = displaySettings.readEntry("DimDisplayWhenIdle", true);
m_screenOffTime = displaySettings.readEntry("TurnOffDisplayIdleTimeoutSec", 60);
m_screenOff = displaySettings.readEntry("TurnOffDisplayWhenIdle", true);
} else {
qDebug() << "[Battery][Display] Group is not listed";
m_dimScreenTime = 30;
m_dimScreen = true;
m_screenOffTime = 60;
m_screenOff = true;
}
if (batteryGroup.hasGroup("SuspendAndShutdown")) {
qDebug() << "[Battery][SuspendAndShutdown] group is listed";
KConfigGroup suspendSessionGroup = batteryGroup.group("SuspendAndShutdown");
m_suspendSessionTime = suspendSessionGroup.readEntry("AutoSuspendIdleTimeoutSec", 300);
} else {
qDebug() << "[Battery][SuspendAndShutdown] is not listed";
m_suspendSessionTime = 300;
}
}
void MobilePower::save()
{
// we set all profiles at the same time, since our UI is a simple global toggle
KConfigGroup acGroup = m_profilesConfig->group("AC");
KConfigGroup batteryGroup = m_profilesConfig->group("Battery");
KConfigGroup lowBatteryGroup = m_profilesConfig->group("LowBattery");
acGroup.group("Display").writeEntry("DimDisplayWhenIdle", m_dimScreen, KConfigGroup::Notify);
acGroup.group("Display").writeEntry("DimDisplayIdleTimeoutSec", m_dimScreenTime, KConfigGroup::Notify);
batteryGroup.group("Display").writeEntry("DimDisplayWhenIdle", m_dimScreen, KConfigGroup::Notify);
batteryGroup.group("Display").writeEntry("DimDisplayIdleTimeoutSec", m_dimScreenTime, KConfigGroup::Notify);
lowBatteryGroup.group("Display").writeEntry("DimDisplayWhenIdle", m_dimScreen, KConfigGroup::Notify);
lowBatteryGroup.group("Display").writeEntry("DimDisplayIdleTimeoutSec", m_dimScreenTime, KConfigGroup::Notify);
acGroup.group("Display").writeEntry("TurnOffDisplayWhenIdle", m_screenOff, KConfigGroup::Notify);
acGroup.group("Display").writeEntry("TurnOffDisplayIdleTimeoutSec", m_screenOffTime, KConfigGroup::Notify);
batteryGroup.group("Display").writeEntry("TurnOffDisplayWhenIdle", m_screenOff, KConfigGroup::Notify);
batteryGroup.group("Display").writeEntry("TurnOffDisplayIdleTimeoutSec", m_screenOffTime, KConfigGroup::Notify);
lowBatteryGroup.group("Display").writeEntry("TurnOffDisplayWhenIdle", m_screenOff, KConfigGroup::Notify);
lowBatteryGroup.group("Display").writeEntry("TurnOffDisplayIdleTimeoutSec", m_screenOffTime, KConfigGroup::Notify);
acGroup.group("SuspendAndShutdown").writeEntry("AutoSuspendIdleTimeoutSec", m_suspendSessionTime, KConfigGroup::Notify);
batteryGroup.group("SuspendAndShutdown").writeEntry("AutoSuspendIdleTimeoutSec", m_suspendSessionTime, KConfigGroup::Notify);
lowBatteryGroup.group("SuspendAndShutdown").writeEntry("AutoSuspendIdleTimeoutSec", m_suspendSessionTime, KConfigGroup::Notify);
m_profilesConfig->sync();
QDBusMessage call =
QDBusMessage::createMethodCall("org.kde.Solid.PowerManagement", "/org/kde/Solid/PowerManagement", "org.kde.Solid.PowerManagement", "refreshStatus");
QDBusConnection::sessionBus().asyncCall(call);
}
QStringList MobilePower::timeOptions() const
{
return timeValues;
}
void MobilePower::setDimScreenIdx(int idx)
{
qreal value = idxToSeconds.value(idx);
qDebug() << "Got the value" << value;
if (m_dimScreenTime == value) {
return;
}
if (value == 0) {
qDebug() << "Setting to never dim";
m_dimScreen = false;
} else {
qDebug() << "Setting to dim in " << value << "Minutes";
m_dimScreen = true;
}
m_dimScreenTime = value;
Q_EMIT dimScreenIdxChanged();
save();
}
void MobilePower::setScreenOffIdx(int idx)
{
qreal value = idxToSeconds.value(idx);
qDebug() << "Got the value" << value;
if (m_screenOffTime == value) {
return;
}
if (value == 0) {
qDebug() << "Setting to never screen off";
m_screenOff = false;
} else {
qDebug() << "Setting to screen off in " << value << "Minutes";
m_screenOff = true;
}
m_screenOffTime = value;
Q_EMIT screenOffIdxChanged();
save();
}
void MobilePower::setSuspendSessionIdx(int idx)
{
qreal value = idxToSeconds.value(idx);
qDebug() << "Got the value" << value;
if (m_suspendSessionTime == value) {
return;
}
if (value == 0) {
qDebug() << "Setting to never suspend";
} else {
qDebug() << "Setting to suspend in " << value << "Minutes";
}
m_suspendSessionTime = value;
Q_EMIT suspendSessionIdxChanged();
save();
}
int MobilePower::suspendSessionIdx()
{
if (m_suspendSessionTime == 0) {
return NEVER;
}
return idxToSeconds.key(std::round(m_suspendSessionTime));
}
int MobilePower::dimScreenIdx()
{
if (!m_dimScreen) {
return NEVER;
}
return idxToSeconds.key(std::round(m_dimScreenTime));
}
int MobilePower::screenOffIdx()
{
if (!m_screenOff) {
return NEVER;
}
return idxToSeconds.key(std::round(m_screenOffTime));
}
BatteryModel *MobilePower::batteries()
{
return m_batteries;
}
#include "mobilepower.moc"

View file

@ -1,52 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org>
#pragma once
#include "batterymodel.h"
#include <KQuickConfigModule>
#include <KSharedConfig>
#include <memory>
class MobilePower : public KQuickConfigModule
{
Q_OBJECT
Q_PROPERTY(BatteryModel *batteries READ batteries CONSTANT)
Q_PROPERTY(int dimScreenIdx READ dimScreenIdx WRITE setDimScreenIdx NOTIFY dimScreenIdxChanged)
Q_PROPERTY(int screenOffIdx READ screenOffIdx WRITE setScreenOffIdx NOTIFY screenOffIdxChanged)
Q_PROPERTY(int suspendSessionIdx READ suspendSessionIdx WRITE setSuspendSessionIdx NOTIFY suspendSessionIdxChanged)
public:
MobilePower(QObject *parent, const KPluginMetaData &metaData);
Q_INVOKABLE QStringList timeOptions() const;
void setDimScreenIdx(int idx);
void setScreenOffIdx(int idx);
void setSuspendSessionIdx(int idx);
int dimScreenIdx();
int screenOffIdx();
int suspendSessionIdx();
BatteryModel *batteries();
Q_SIGNAL void dimScreenIdxChanged();
Q_SIGNAL void screenOffIdxChanged();
Q_SIGNAL void suspendSessionIdxChanged();
QString stringForValue(int value);
void load() override;
void save() override;
private:
BatteryModel *m_batteries;
KSharedConfig::Ptr m_profilesConfig;
int m_suspendSessionTime;
int m_dimScreenTime;
bool m_dimScreen;
int m_screenOffTime;
bool m_screenOff;
};

View file

@ -1,188 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015 David Edmundson <david@davidedmundson.co.uk>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/
#include "statisticsprovider.h"
#include <QDBusArgument>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusMetaType> // qDBusRegisterMetaType
#include <QDBusPendingReply>
#include <QDebug>
const QDBusArgument &operator<<(QDBusArgument &argument, const HistoryReply &data)
{
argument.beginStructure();
argument << data.time << data.value << data.charging;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &arg, HistoryReply &attrs)
{
arg.beginStructure();
arg >> attrs.time >> attrs.value >> attrs.charging;
arg.endStructure();
return arg;
}
StatisticsProvider::StatisticsProvider(QObject *parent)
: QObject(parent)
{
m_type = StatisticsProvider::ChargeType;
m_duration = 120;
qDBusRegisterMetaType<HistoryReply>();
qDBusRegisterMetaType<QList<HistoryReply>>();
}
void StatisticsProvider::setDevice(const QString &device)
{
if (device == m_device) {
return;
}
m_device = device;
Q_EMIT deviceChanged();
load();
}
void StatisticsProvider::setDuration(uint duration)
{
if (duration == m_duration) {
return;
}
m_duration = duration;
Q_EMIT durationChanged();
load();
}
void StatisticsProvider::setType(StatisticsProvider::HistoryType type)
{
if (m_type == type) {
return;
}
m_type = type;
Q_EMIT typeChanged();
load();
}
void StatisticsProvider::classBegin()
{
}
void StatisticsProvider::componentComplete()
{
m_isComplete = true;
load();
}
QVariantList StatisticsProvider::asPoints() const
{
QVariantList points;
points.reserve(m_values.count());
foreach (const HistoryReply &h, m_values) {
points.append(QPointF(h.time, h.value));
}
if (!points.isEmpty()) {
points.takeLast();
}
return points;
}
int StatisticsProvider::count() const
{
return m_values.count();
}
int StatisticsProvider::firstDataPointTime() const
{
if (m_values.isEmpty()) {
return 0;
}
return m_values.first().time;
}
int StatisticsProvider::lastDataPointTime() const
{
if (m_values.isEmpty()) {
return 0;
}
return m_values.last().time;
}
int StatisticsProvider::largestValue() const
{
if (m_values.isEmpty()) {
return 0;
}
int max = 0; // TODO std::max or something?
for (auto it = m_values.constBegin(), end = m_values.constEnd(); it != end; ++it) {
if ((*it).value > max) {
max = (*it).value;
}
}
return max;
}
void StatisticsProvider::refresh()
{
load();
}
void StatisticsProvider::load()
{
if (!m_isComplete || m_device.isEmpty()) {
return;
}
auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.UPower"),
m_device,
QStringLiteral("org.freedesktop.UPower.Device"),
QStringLiteral("GetHistory"));
if (m_type == RateType) {
msg << QLatin1String("rate");
} else { // m_type must = ChargeType
msg << QLatin1String("charge");
}
uint resolution = 100;
msg << m_duration << resolution;
QDBusPendingReply<QList<HistoryReply>> reply = QDBusConnection::systemBus().asyncCall(msg);
auto *watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QList<HistoryReply>> reply = *watcher;
watcher->deleteLater();
m_values.clear();
if (reply.isError()) {
qWarning() << "Failed to get device history from UPower" << reply.error().message();
return;
}
foreach (const HistoryReply &r, reply.value()) {
if (r.value > 0) { // we get back some values which contain no value, possibly to indicate if charging changes, ignore them
m_values.prepend(r);
}
}
Q_EMIT dataChanged();
});
}

View file

@ -1,89 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015 David Edmundson <david@davidedmundson.co.uk>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/
#ifndef STATISTICSPROVIDER_H
#define STATISTICSPROVIDER_H
#include <QObject>
#include <QPointF>
#include <QQmlParserStatus>
struct HistoryReply {
public:
uint time = 0;
double value = 0.0;
uint charging = 0;
};
Q_DECLARE_METATYPE(HistoryReply)
class StatisticsProvider : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString device MEMBER m_device WRITE setDevice NOTIFY deviceChanged)
Q_PROPERTY(uint duration MEMBER m_duration WRITE setDuration NOTIFY durationChanged)
Q_PROPERTY(HistoryType type MEMBER m_type WRITE setType NOTIFY typeChanged)
Q_PROPERTY(QVariantList points READ asPoints NOTIFY dataChanged)
Q_PROPERTY(int count READ count NOTIFY dataChanged)
Q_PROPERTY(int firstDataPointTime READ firstDataPointTime NOTIFY dataChanged)
Q_PROPERTY(int lastDataPointTime READ lastDataPointTime NOTIFY dataChanged)
Q_PROPERTY(int largestValue READ largestValue NOTIFY dataChanged)
public:
enum HistoryType {
RateType,
ChargeType,
};
Q_ENUM(HistoryType)
enum HistoryRoles {
TimeRole = Qt::UserRole + 1,
ValueRole,
ChargingRole,
};
explicit StatisticsProvider(QObject *parent = nullptr);
void setDevice(const QString &device);
void setDuration(uint duration);
void setType(HistoryType type);
void load();
void classBegin() override;
void componentComplete() override;
QVariantList asPoints() const;
int count() const;
int firstDataPointTime() const;
int lastDataPointTime() const;
int largestValue() const;
Q_SIGNALS:
void deviceChanged();
void typeChanged();
void durationChanged();
void dataChanged();
public Q_SLOTS:
void refresh();
private:
QString m_device;
HistoryType m_type;
uint m_duration; // in seconds
QList<HistoryReply> m_values;
bool m_isComplete = false;
};
#endif // STATISTICSPROVIDER_H

View file

@ -1,161 +0,0 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2023 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.plasma.components 3 as PlasmaComponents
import org.kde.kcmutils
import org.kde.kirigamiaddons.formcard 1 as FormCard
import org.kde.kcm.power.mobile.private
FormCard.FormCardPage {
id: root
property QtObject battery
property string vendor
property string product
property string currentUdi
title: i18n("Battery Information")
data: HistoryModel {
id: history
duration: 86400 // last 24 hours
device: currentUdi
type: HistoryModel.ChargeType
}
FormCard.FormHeader {
title: i18n("Usage Graph")
visible: history.count > 1
}
FormCard.FormCard {
visible: history.count > 1
FormCard.AbstractFormDelegate {
background: null
clip: true
contentItem: Flickable {
implicitWidth: 500
implicitHeight: 200
contentWidth: 500
contentHeight: 200
Graph {
id: graph
width: 500
height: 200
implicitWidth: 500
implicitHeight: 200
data: history.points
// Set grid lines distances which directly correspondent to the xTicksAt variables
readonly property var xDivisionWidths: [1000 * 60 * 10, 1000 * 60 * 60 * 12, 1000 * 60 * 60, 1000 * 60 * 30, 1000 * 60 * 60 * 2, 1000 * 60 * 10]
xTicksAt: graph.xTicksAtFullSecondHour
xDivisionWidth: xDivisionWidths[xTicksAt]
xMin: history.firstDataPointTime
xMax: history.lastDataPointTime
xDuration: history.duration
yMax: 100
yStep: 20
visible: history.count > 1
}
}
}
}
FormCard.FormHeader {
title: i18n("Information")
}
FormCard.FormCard {
FormCard.FormTextDelegate {
id: isRechargeableDelegate
text: i18n("Is Rechargeable")
description: battery.rechargeable ? i18n("Yes") : i18n("No")
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: chargeStateDelegate
text: i18n("Charge State")
description: {
switch (battery.chargeState) {
case Battery.NoCharge: return i18n("Not charging")
case Battery.Charging: return i18n("Charging")
case Battery.Discharging: return i18n("Discharging")
case Battery.FullyCharged: return i18n("Fully charged")
default: return i18n("Unknown")
}
}
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: currentChargeDelegate
text: i18n("Current Charge")
description: i18nc("%1 is percentage value", "%1 %", Number(battery.chargePercent).toLocaleString(Qt.locale(), "f", 0))
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: healthDelegate
text: i18n("Health")
description: i18nc("%1 is percentage value", "%1 %", Number(battery.capacity).toLocaleString(Qt.locale(), "f", 0))
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: vendorDelegate
text: i18n("Vendor")
description: root.vendor
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: modelDelegate
text: i18n("Model")
description: root.product
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: serialDelegate
text: i18n("Serial Number")
description: battery.serial
}
FormCard.FormDelegateSeparator {}
FormCard.FormTextDelegate {
id: technologyDelegate
text: i18n("Technology")
description: {
switch (battery.technology) {
case Battery.LithiumIon: return i18n("Lithium ion")
case Battery.LithiumPolymer: return i18n("Lithium polymer")
case Battery.LithiumIronPhosphate: return i18n("Lithium iron phosphate")
case Battery.LeadAcid: return i18n("Lead acid")
case Battery.NickelCadmium: return i18n("Nickel cadmium")
case Battery.NickelMetalHydride: return i18n("Nickel metal hydride")
default: return i18n("Unknown technology")
}
}
}
}
}

View file

@ -1,240 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015 David Edmundson <david@davidedmundson.co.uk>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/
import QtQuick 2.3
/**
* We need to draw a graph, all other libs are not suitable as we are basically
* a connected scatter plot with non linear X spacing.
* Currently this is not available in kdeclarative nor kqtquickcharts
*
* We only paint once, so canvas is fast enough for our purposes.
* It is designed to look identical to those in ksysguard.
*/
Canvas
{
id: canvas
antialiasing: true
readonly property real xTicksAtDontCare: 0
readonly property real xTicksAtTwelveOClock: 1
readonly property real xTicksAtFullHour: 2
readonly property real xTicksAtHalfHour: 3
readonly property real xTicksAtFullSecondHour: 4
readonly property real xTicksAtTenMinutes: 5
readonly property real xTicksAtFullTwoHours: 6
property int xPadding: 45
property int yPadding: 40
property var data //expect an array of QPointF
property real yMax: 100
property real xMax: 100
property real yMin: 0
property real xMin: 0
property real yStep: 20
property real xDuration: 3600
property real xDivisions: 6
property real xDivisionWidth: 600000
property real xTicksAt: xTicksAtDontCare
//internal
property real plotWidth: width - xPadding * 1.5
property real plotHeight: height - yPadding * 2
onDataChanged: {
canvas.requestPaint();
}
//take a QPointF
function scalePoint(plot, currentUnixTime) {
var scaledX = (plot.x - (currentUnixTime / 1000 - xDuration)) / xDuration * plotWidth;
var scaledY = (plot.y - yMin) * plotHeight / (yMax - yMin);
return Qt.point(xPadding + scaledX,
height - yPadding - scaledY);
}
SystemPalette {
id: palette;
colorGroup: SystemPalette.Active
}
onPaint: {
var c = canvas.getContext('2d');
c.clearRect(0,0, width, height)
//draw the background
c.fillStyle = palette.base
c.fillRect(xPadding, yPadding, plotWidth, plotHeight);
//reset for fonts and stuff
c.fillStyle = palette.text
//Draw the lines
c.lineWidth = 1;
c.lineJoin = 'round';
c.lineCap = 'round';
c.strokeStyle = 'rgba(255, 0, 0, 1)';
var gradient = c.createLinearGradient(0,0,0,height);
gradient.addColorStop(0, 'rgba(255, 0, 0, 0.2)');
gradient.addColorStop(1, 'rgba(255, 0, 0, 0.05)');
c.fillStyle = gradient;
// For scaling
var currentUnixTime = Date.now()
var xMinUnixTime = currentUnixTime - xDuration * 1000
// Draw the line graph
c.beginPath();
var index = 0
while ((index < data.length - 1) && (data[index].x < (xMinUnixTime / 1000))) {
index++
}
var firstPoint = scalePoint(data[index], currentUnixTime)
c.moveTo(firstPoint.x, firstPoint.y)
var point
for (var i = index + 1; i < data.length; i++) {
if (data[i].x > (xMinUnixTime / 1000)) {
point = scalePoint(data[i], currentUnixTime)
c.lineTo(point.x, point.y)
}
}
c.stroke();
c.strokeStyle = 'rgba(0, 0, 0, 0)';
c.lineTo(point.x, height - yPadding);
c.lineTo(firstPoint.x, height - yPadding);
c.fill();
c.closePath()
// Draw the frame on top
//draw an outline
c.strokeStyle = 'rgba(0,50,0,0.02)';
c.lineWidth = 1;
c.rect(xPadding - 1, yPadding - 1, plotWidth + 2, plotHeight + 2);
// Draw the Y value texts
c.fillStyle = palette.text;
c.textAlign = "right"
c.textBaseline = "middle";
for(var i = 0; i <= yMax; i += yStep) {
var y = scalePoint(Qt.point(0,i)).y;
c.fillText(i18nc("%1 is a percentage value", "%1%", i), xPadding - 10, y);
//grid line
c.moveTo(xPadding, y)
c.lineTo(plotWidth + xPadding, y)
}
c.stroke()
// Draw the X value texts
c.textAlign = "center"
c.lineWidth = 1
c.strokeStyle = 'rgba(0, 0, 0, 0.15)'
var xDivisions = xDuration / xDivisionWidth * 1000
var xGridDistance = plotWidth / xDivisions
var xTickPos
var xTickDateTime
var xTickDateStr
var xTickTimeStr
var currentDateTime = new Date()
var lastDateStr = currentDateTime.toLocaleDateString(Qt.locale(), Locale.ShortFormat)
var hours = currentDateTime.getHours()
var minutes = currentDateTime.getMinutes()
var seconds = currentDateTime.getSeconds()
var diff
switch (xTicksAt) {
case xTicksAtTwelveOClock:
diff = ((hours - 12) * 60 * 60 + minutes * 60 + seconds)
break
case xTicksAtFullHour:
diff = (minutes * 60 + seconds)
break
case xTicksAtFullSecondHour:
diff = (minutes * 60 + seconds)
break
case xTicksAtHalfHour:
diff = ((minutes - 30) * 60 + seconds)
break
case xTicksAtTenMinutes:
diff = ((minutes % 10) * 60 + seconds)
break
default:
diff = 0
}
var xGridOffset = plotWidth * (diff / xDuration)
var dateChanged = false
var dashedLines = 50
var dashedLineLength = plotHeight / dashedLines
var dashedLineDutyCycle
for (var i = xDivisions; i >= -1; i--) {
xTickPos = i * xGridDistance + xPadding - xGridOffset
if ((xTickPos > xPadding) && (xTickPos < plotWidth + xPadding))
{
xTickDateTime = new Date(currentUnixTime - (xDivisions - i) * xDivisionWidth - diff * 1000)
xTickDateStr = xTickDateTime.toLocaleDateString(Qt.locale(), Locale.ShortFormat)
xTickTimeStr = xTickDateTime.toLocaleTimeString(Qt.locale(), Locale.ShortFormat)
if (lastDateStr != xTickDateStr) {
dateChanged = true
}
if ((i % 2 == 0) || (xDivisions < 10))
{
// Display the time
c.fillText(xTickTimeStr, xTickPos, canvas.height - yPadding / 2)
// If the date has changed and is not the current day in a <= 24h graph, display it
// Always display the date for 48h and 1 week graphs
if (dateChanged || (xDuration > (60*60*48))) {
c.fillText(xTickDateStr, xTickPos, canvas.height - yPadding / 4)
dateChanged = false
}
// Tick markers
c.moveTo(xTickPos, canvas.height - yPadding)
c.lineTo(xTickPos, canvas.height - (yPadding * 4) / 5)
dashedLineDutyCycle = 0.5
} else {
dashedLineDutyCycle = 0.1
}
for (var j = 0; j < dashedLines; j++) {
c.moveTo(xTickPos, yPadding + j * dashedLineLength)
c.lineTo(xTickPos, yPadding + j * dashedLineLength + dashedLineDutyCycle * dashedLineLength)
}
lastDateStr = xTickDateStr
}
}
c.stroke()
}
}

View file

@ -1,148 +0,0 @@
/*
SPDX-FileCopyrightText: 2011 Sebastian Kügler <sebas@kde.org>
SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.2
import QtQuick.Controls 2.10 as QQC2
import QtQuick.Layouts 1.11
import org.kde.kirigami 2.10 as Kirigami
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.kcmutils
import org.kde.kirigamiaddons.formcard 1.0 as FormCard
import org.kde.kcm.power.mobile.private 1.0
SimpleKCM {
id: powermanagementModule
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: Kirigami.Units.gridUnit
ColumnLayout {
width: parent.width
spacing: 0
FormCard.FormHeader {
title: i18n("Devices")
}
FormCard.FormCard {
Repeater {
model: kcm.batteries
delegate: FormCard.AbstractFormDelegate {
Layout.fillWidth: true
onClicked: kcm.push("BatteryPage.qml", { "battery": model.battery, "vendor": model.vendor, "product": model.product, "currentUdi": model.udi })
contentItem: RowLayout {
spacing: Kirigami.Units.gridUnit
Kirigami.Icon {
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: Kirigami.Units.iconSizes.smallMedium
Layout.rightMargin: Kirigami.Units.largeSpacing
source: {
switch (model.battery.type) {
case 3: return model.battery.chargeState === 1 ? "battery-full-charging" : "battery-full"
case 2: return "battery-ups"
case 9: return "monitor"
case 4: return "input-mouse"
case 5: return "input-keyboard"
case 1: return "phone"
case 7: return "smartphone"
default: return "paint-unknown"
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
QQC2.Label {
Layout.fillWidth: true
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 2
color: Kirigami.Theme.textColor
text: {
let batteryType;
switch (model.battery.type) {
case 3: batteryType = i18n("Internal battery"); break;
case 2: batteryType = i18n("UPS battery"); break;
case 9: batteryType = i18n("Monitor battery"); break;
case 4: batteryType = i18n("Mouse battery"); break;
case 5: batteryType = i18n("Keyboard battery"); break;
case 1: batteryType = i18n("PDA battery"); break;
case 7: batteryType = i18n("Phone battery"); break;
default: batteryType = i18n("Unknown battery"); break;
}
const chargePercent = i18nc("%1 is the charge percent, % is the percent sign", "%1%", Number(battery.chargePercent).toLocaleString(Qt.locale(), "f", 0));
return (model.battery.chargeState === Battery.Charging) ? i18nc("%1 is battery type, %2 is charge percent", "%1 %2 (Charging)", batteryType, chargePercent) : i18nc("%1 is battery type, %2 is charge percent", "%1 %2", batteryType, chargePercent);
}
}
QQC2.ProgressBar {
Layout.fillWidth: true
from: 0
to: 100
value: model.battery.chargePercent
}
}
Kirigami.Icon {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
source: "arrow-right"
implicitWidth: Math.round(Kirigami.Units.iconSizes.small * 0.75)
implicitHeight: Math.round(Kirigami.Units.iconSizes.small * 0.75)
}
}
}
}
}
FormCard.FormHeader {
title: i18n("Screen")
}
FormCard.FormCard {
FormCard.FormComboBoxDelegate {
id: dimScreenCombo
text: i18nc("Part of a sentence like 'Dim screen after 5 minutes'", "Dim screen after")
model: kcm.timeOptions()
currentIndex: kcm.dimScreenIdx
onCurrentIndexChanged: kcm.dimScreenIdx = currentIndex
}
FormCard.FormDelegateSeparator { above: dimScreenCombo; below: screenOffCombo }
FormCard.FormComboBoxDelegate {
id: screenOffCombo
text: i18nc("Part of a sentence like 'Turn off screen after 5 minutes'", "Turn off screen after")
model: kcm.timeOptions()
currentIndex: kcm.screenOffIdx
onCurrentIndexChanged: kcm.screenOffIdx = currentIndex
}
FormCard.FormDelegateSeparator { above: screenOffCombo; below: suspendCombo }
FormCard.FormComboBoxDelegate {
id: suspendCombo
text: i18nc("Part of a sentence like 'Suspend device after 5 minutes'", "Suspend device after")
model: kcm.timeOptions()
currentIndex: kcm.suspendSessionIdx
onCurrentIndexChanged: kcm.suspendSessionIdx = currentIndex
}
}
}
}