Very early KDE 6 release.

This commit is contained in:
wackyideas 2024-08-09 03:20:25 +02:00
parent 7cc4ccabbc
commit 686046d4f7
6272 changed files with 140920 additions and 529657 deletions

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name=""/>
<group name="General">
<entry name="showPercentage" type="bool">
<label>If true, the battery will display a little charge percentage label inside.</label>
<default>false</default>
</entry>
</group>
</kcfg>

View file

@ -0,0 +1,103 @@
/*
* SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
* SPDX-FileCopyrightText: 2013 Kai Uwe Broulik <kde@privat.broulik.de>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.0
import org.kde.kirigami 2.20 as Kirigami
Item {
id: root
property bool hasBattery
property int percent
property bool pluggedIn
property string batteryType
property bool active: false
property string powerProfileIconName: ""
property int health
property bool broken
// Icon for current charge level, charging status, and optionally power
// profile indication (for batteries that support it by setting
// "powerProfileIconName" to something other than an empty string).
Kirigami.Icon {
anchors.fill: parent
source: root.hasBattery ? fillElement(root.percent, root.health, root.broken) : "battery-missing"
visible: !otherBatteriesIcon.visible
active: root.active
function fillElement(p: int, h: int, b: bool): string {
let name
if(b && typeof h !== "undefined") {
return "battery-missing"
}
if (p >= 95) {
name = "battery-100";
} else if (p >= 85) {
name = "battery-090";
} else if (p >= 75) {
name = "battery-080";
} else if (p >= 65) {
name = "battery-070";
} else if (p >= 55) {
name = "battery-060";
} else if (p >= 45) {
name = "battery-050";
} else if (p >= 35) {
name = "battery-040";
} else if (p >= 25) {
name = "battery-030";
} else if (p >= 15) {
name = "battery-020";
} else if (p > 5) {
name = "battery-010";
} else {
name = "battery-000";
}
if (root.pluggedIn) {
name += "-charging";
}
return name;
}
}
// Generic icon for other types of batteries
Kirigami.Icon {
id: otherBatteriesIcon
anchors.fill: parent
source: elementForType(root.batteryType)
visible: source !== ""
active: root.active
function elementForType(t: string): string {
switch(t) {
case "Mouse":
return "input-mouse-battery";
case "Keyboard":
return "input-keyboard-battery";
case "Pda":
return "phone-battery";
case "Phone":
return "phone-battery";
case "Ups":
return "battery-ups";
case "GamingInput":
return "input-gaming-battery";
case "Bluetooth":
return "preferences-system-bluetooth-battery";
case "Headset":
return "audio-headset-battery";
default:
return "";
}
}
}
}

View file

@ -0,0 +1,276 @@
/*
SPDX-FileCopyrightText: 2012-2013 Daniel Nicoletti <dantti12@gmail.com>
SPDX-FileCopyrightText: 2013-2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import org.kde.coreaddons as KCoreAddons
import org.kde.plasma.components as PlasmaComponents3
//import org.kde.plasma.workspace.components
import org.kde.kirigami as Kirigami
import "logic.js" as Logic
PlasmaComponents3.ItemDelegate {
id: root
// We'd love to use `required` properties, especially since the model provides role names for them;
// but unfortunately some of those roles have whitespaces in their name, which QML doesn't have any
// workaround for (raw identifiers like r#try in Rust would've helped here).
//
// type: {
// Capacity: int,
// Energy: real,
// "Is Power Supply": bool,
// Percent: int,
// "Plugged In": bool,
// "Pretty Name": string,
// Product: string,
// State: "Discharging"|"Charging"|"FullyCharged"|etc.,
// Type: string,
// Vendor: string,
// }?
property var battery
// NOTE: According to the UPower spec this property is only valid for primary batteries, however
// UPower seems to set the Present property false when a device is added but not probed yet
readonly property bool isPresent: root.battery["Plugged in"]
readonly property bool isPowerSupply: root.battery["Is Power Supply"]
readonly property bool isBroken: root.battery.Capacity > 0 && root.battery.Capacity < 50
property int remainingTime: 0
// Existing instance of a slider to use as a reference to calculate extra
// margins for a progress bar, so that the row of labels on top of it
// could visually look as if it were on the same distance from the bar as
// they are from the slider.
property PlasmaComponents3.Slider matchHeightOfSlider: PlasmaComponents3.Slider {}
readonly property real extraMargin: Math.max(0, Math.floor((matchHeightOfSlider.height/* - chargeBar.height*/) / 2))
highlighted: false
down: false
hoverEnabled: false
text: battery["Pretty Name"]
property int batteryIndex
property string detailsDescription: {
let description = [];
for (let i = 0; i < children.length; i++) {
if (children[i].visible && children[i].hasOwnProperty("text")) {
description.push(children[i].text);
}
}
return description.join(" ");
}
Accessible.description: `${Logic.stringForBatteryState(root.battery, pmSource)} ${i18nc("Placeholder is battery percentage", "%1%", root.battery.Percent)}; ${detailsDescription}`
contentItem: RowLayout {
//spacing: Kirigami.Units.gridUnit
FlyoutBatteryIcon {
id: batteryIcon
visible: batteryIndex === 0
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
batteryType: root.battery.Type
percent: root.battery.Percent
hasBattery: root.isPresent
pluggedIn: root.battery.State === "Charging" && root.battery["Is Power Supply"]
broken: root.isBroken
health: root.battery.Capacity
}
PlasmaComponents3.Label {
id: batteryName
//visible: batteryIndex !== 0
Layout.fillWidth: true
Layout.fillHeight: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
wrapMode: Text.Wrap
readonly property var chargeStopThreshold: pmSource.data["Battery"] ? pmSource.data["Battery"]["Charge Stop Threshold"] : undefined
readonly property bool pluggedIn: pmSource.data["AC Adapter"] !== undefined && pmSource.data["AC Adapter"]["Plugged in"]
text: {
if(batteryIndex === 0) {
var txt = ""
if(!root.isPresent)
txt = "No battery is detected"
else if(root.battery.State == "FullyCharged" || batterymonitor.isSomehowFullyCharged)
txt = `Fully charged (${root.battery.Percent}%)`
else
txt = root.battery.Percent + "% available" + (root.battery.State == "Charging" ? " (plugged in, charging)" : "")
if(pluggedIn && root.isPowerSupply && typeof chargeStopThreshold === "number" && chargeStopThreshold > 0 && chargeStopThreshold < 100)
txt += "\n" + i18n("Battery is configured to charge up to approximately %1%.", chargeStopThreshold || 0)
if(root.isBroken && typeof root.battery.Capacity !== "undefined")
txt += "\nConsider replacing your battery."
return txt;
} else {
return `${root.text} #${batteryIndex}: ` + root.isPresent ? "Not present" : (root.battery.Percent + "% available")
}
}
}
/*ColumnLayout {
Layout.fillWidth: true
Layout.alignment: root.isPresent ? Qt.AlignTop : Qt.AlignVCenter
spacing: 0
RowLayout {
spacing: Kirigami.Units.smallSpacing
PlasmaComponents3.Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: root.text
}
PlasmaComponents3.Label {
id: isPowerSupplyLabel
text: Logic.stringForBatteryState(root.battery, pmSource)
// For non-power supply batteries only show label for known-good states
visible: root.isPowerSupply || ["Discharging", "FullyCharged", "Charging"].includes(root.battery.State)
enabled: false
}
PlasmaComponents3.Label {
id: percentLabel
horizontalAlignment: Text.AlignRight
visible: root.isPresent
text: i18nc("Placeholder is battery percentage", "%1%", root.battery.Percent)
}
}
PlasmaComponents3.ProgressBar {
id: chargeBar
Layout.fillWidth: true
Layout.topMargin: root.extraMargin
Layout.bottomMargin: root.extraMargin
from: 0
to: 100
visible: root.isPresent
value: Number(root.battery.Percent)
}
// This gridLayout basically emulates an at-most-two-rows table with a
// single wide fillWidth/columnSpan header. Not really worth it trying
// to refactor it into some more clever fancy model-delegate stuff.
GridLayout {
id: details
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing
columns: 2
columnSpacing: Kirigami.Units.smallSpacing
rowSpacing: 0
Accessible.description: {
let description = [];
for (let i = 0; i < children.length; i++) {
if (children[i].visible && children[i].hasOwnProperty("text")) {
description.push(children[i].text);
}
}
return description.join(" ");
}
component LeftLabel : PlasmaComponents3.Label {
// fillWidth is true, so using internal alignment
horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true
font: Kirigami.Theme.smallFont
wrapMode: Text.WordWrap
enabled: false
}
component RightLabel : PlasmaComponents3.Label {
// fillWidth is false, so using external (grid-cell-internal) alignment
Layout.alignment: Qt.AlignRight
Layout.fillWidth: false
font: Kirigami.Theme.smallFont
enabled: false
}
PlasmaComponents3.Label {
Layout.fillWidth: true
Layout.columnSpan: 2
text: root.isBroken && typeof root.battery.Capacity !== "undefined"
? i18n("This battery's health is at only %1% and it should be replaced. Contact the manufacturer.", root.battery.Capacity)
: ""
font: Kirigami.Theme.smallFont
color: Kirigami.Theme.neutralTextColor
visible: root.isBroken
wrapMode: Text.WordWrap
}
readonly property bool remainingTimeRowVisible: root.battery !== null
&& root.remainingTime > 0
&& root.battery["Is Power Supply"]
&& ["Discharging", "Charging"].includes(root.battery.State)
readonly property bool isEstimatingRemainingTime: root.battery !== null
&& root.isPowerSupply
&& root.remainingTime === 0
&& root.battery.State === "Discharging"
LeftLabel {
text: root.battery.State === "Charging"
? i18n("Time To Full:")
: i18n("Remaining Time:")
visible: details.remainingTimeRowVisible || details.isEstimatingRemainingTime
}
RightLabel {
text: details.isEstimatingRemainingTime ? i18nc("@info", "Estimating…")
: KCoreAddons.Format.formatDuration(root.remainingTime, KCoreAddons.FormatTypes.HideSeconds)
visible: details.remainingTimeRowVisible || details.isEstimatingRemainingTime
}
readonly property bool healthRowVisible: root.battery !== null
&& root.battery["Is Power Supply"]
&& root.battery.Capacity !== ""
&& typeof root.battery.Capacity === "number"
&& !root.isBroken
LeftLabel {
text: i18n("Battery Health:")
visible: details.healthRowVisible
}
RightLabel {
text: details.healthRowVisible
? i18nc("Placeholder is battery health percentage", "%1%", root.battery.Capacity)
: ""
visible: details.healthRowVisible
}
}
InhibitionHint {
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing
readonly property var chargeStopThreshold: pmSource.data["Battery"] ? pmSource.data["Battery"]["Charge Stop Threshold"] : undefined
readonly property bool pluggedIn: pmSource.data["AC Adapter"] !== undefined && pmSource.data["AC Adapter"]["Plugged in"]
visible: pluggedIn && root.isPowerSupply && typeof chargeStopThreshold === "number" && chargeStopThreshold > 0 && chargeStopThreshold < 100
iconSource: "kt-speed-limits" // FIXME good icon
text: i18n("Battery is configured to charge up to approximately %1%.", chargeStopThreshold || 0)
}
}*/
}
}

View file

@ -0,0 +1,113 @@
/*
SPDX-FileCopyrightText: 2011 Sebastian Kügler <sebas@kde.org>
SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
SPDX-FileCopyrightText: 2013 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import org.kde.plasma.plasmoid
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.workspace.components as WorkspaceComponents
import org.kde.kirigami as Kirigami
MouseArea {
id: root
property real itemSize: Math.min(root.height, root.width/view.count)
readonly property bool isConstrained: Plasmoid.formFactor === PlasmaCore.Types.Vertical || Plasmoid.formFactor === PlasmaCore.Types.Horizontal
property real brightnessError: 0
property QtObject batteries
property bool hasBatteries: false
required property bool isSetToPerformanceMode
required property bool isSetToPowerSaveMode
required property bool isSomehowFullyCharged
activeFocusOnTab: true
hoverEnabled: true
property bool wasExpanded
Accessible.name: Plasmoid.title
Accessible.description: `${toolTipMainText}; ${toolTipSubText}`
Accessible.role: Accessible.Button
onPressed: wasExpanded = batterymonitor.expanded
onClicked: batterymonitor.expanded = !wasExpanded
// "No Batteries" case
Kirigami.Icon {
anchors.fill: parent
visible: !root.hasBatteries
source: Plasmoid.icon
active: root.containsMouse
}
// We have any batteries; show their status
//Should we consider turning this into a Flow item?
Row {
visible: root.hasBatteries
anchors.centerIn: parent
Repeater {
id: view
model: root.isConstrained ? 1 : root.batteries
Item {
id: batteryContainer
property int percent: root.isConstrained ? pmSource.data["Battery"]["Percent"] : model["Percent"]
property bool pluggedIn: pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"] && (root.isConstrained || model["Is Power Supply"])
height: root.itemSize
width: root.width/view.count
property real iconSize: Math.min(width, height)
// "Held on a Power Profile mode while plugged in" use case; show the
// icon of the active mode so the user can notice this at a glance
/*Kirigami.Icon {
id: powerProfileModeIcon
anchors.fill: parent
visible: batteryContainer.pluggedIn && (root.isSetToPerformanceMode || root.isSetToPowerSaveMode)
source: root.isSetToPerformanceMode
? "battery-profile-performance-symbolic"
: "battery-profile-powersave-symbolic"
active: root.containsMouse
}*/
// Show normal battery icon
BatteryIcon {
id: batteryIcon
anchors.centerIn: parent
height: batteryContainer.iconSize
width: height
active: root.containsMouse
visible: !(batteryContainer.pluggedIn && (root.isSetToPerformanceMode || root.isSetToPowerSaveMode))
hasBattery: root.hasBatteries
percent: batteryContainer.percent
pluggedIn: batteryContainer.pluggedIn
broken: batterymonitor.isBroken
health: batterymonitor.batteryCapacity
}
WorkspaceComponents.BadgeOverlay {
anchors.bottom: parent.bottom
anchors.right: parent.right
visible: Plasmoid.configuration.showPercentage && !root.isSomehowFullyCharged
text: i18nc("battery percentage below battery icon", "%1%", percent)
icon: batteryIcon
}
}
}
}
}

View file

@ -0,0 +1,117 @@
/*
* SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
* SPDX-FileCopyrightText: 2013 Kai Uwe Broulik <kde@privat.broulik.de>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.0
import org.kde.kirigami 2.20 as Kirigami
Item {
id: root
property bool hasBattery
property int percent
property bool pluggedIn
property string batteryType
property bool active: false
property string powerProfileIconName: ""
property int health
property bool broken
// Icon for current charge level, charging status, and optionally power
// profile indication (for batteries that support it by setting
// "powerProfileIconName" to something other than an empty string).
Kirigami.Icon {
id: mainBattery
anchors.fill: parent
source: root.hasBattery ? fillElement(root.percent, root.health, root.broken) : "flyout-battery-missing"
visible: !otherBatteriesIcon.visible
active: root.active
function fillElement(p: int, h: int, b: bool): string {
let name = "flyout-"
if(b && typeof h !== "undefined") {
return "flyout-battery-missing"
}
if (p >= 95) {
name += "battery-100";
} else if (p >= 85) {
name += "battery-090";
} else if (p >= 75) {
name += "battery-080";
} else if (p >= 65) {
name += "battery-070";
} else if (p >= 55) {
name += "battery-060";
} else if (p >= 45) {
name += "battery-050";
} else if (p >= 35) {
name += "battery-040";
} else if (p >= 25) {
name += "battery-030";
} else if (p >= 15) {
name += "battery-020";
} else if (p > 5) {
name += "battery-010";
} else {
name += "battery-000";
}
return name;
}
}
Kirigami.Icon {
anchors.fill: parent
visible: !otherBatteriesIcon.visible && source != ""
active: root.active
source: {
if(batterymonitor.isPluggedIn) {
return "flyout-battery-charging"
} else if(mainBattery.source === "flyout-battery-000") {
return "flyout-battery-critical";
} else if(mainBattery.source === "flyout-battery-010") {
return "flyout-battery-caution";
}
return "";
}
}
// Generic icon for other types of batteries
Kirigami.Icon {
id: otherBatteriesIcon
anchors.fill: parent
source: elementForType(root.batteryType)
visible: source !== ""
active: root.active
function elementForType(t: string): string {
switch(t) {
case "Mouse":
return "input-mouse-battery";
case "Keyboard":
return "input-keyboard-battery";
case "Pda":
return "phone-battery";
case "Phone":
return "phone-battery";
case "Ups":
return "battery-ups";
case "GamingInput":
return "input-gaming-battery";
case "Bluetooth":
return "preferences-system-bluetooth-battery";
case "Headset":
return "audio-headset-battery";
default:
return "";
}
}
}
}

View file

@ -0,0 +1,37 @@
/*
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import org.kde.plasma.components as PlasmaComponents3
import org.kde.kirigami as Kirigami
RowLayout {
property alias iconSource: iconItem.source
property alias text: label.text
property int leftPadding: 0
spacing: Kirigami.Units.smallSpacing
Kirigami.Icon {
id: iconItem
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
Layout.leftMargin: leftPadding
Layout.alignment: Qt.AlignTop
visible: valid
}
PlasmaComponents3.Label {
id: label
Layout.fillWidth: true
//font: Kirigami.Theme.smallFont
wrapMode: Text.WordWrap
elide: Text.ElideRight
maximumLineCount: 4
}
}

View file

@ -0,0 +1,168 @@
/*
SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
SPDX-FileCopyrightText: 2013-2016 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import org.kde.plasma.components as PlasmaComponents3
import org.kde.plasma.extras as PlasmaExtras
import org.kde.kirigami as Kirigami
PlasmaExtras.Representation {
id: dialog
property alias model: batteryRepeater.model
property bool pluggedIn
property int remainingTime
property var profilesInstalled
property string activeProfile
property var profiles
// List of active power management inhibitions (applications that are
// blocking sleep and screen locking).
//
// type: [{
// Icon: string,
// Name: string,
// Reason: string,
// }]
property var inhibitions: []
property bool manuallyInhibited
property bool inhibitsLidAction
property string inhibitionReason
property string degradationReason
// type: [{ Name: string, Icon: string, Profile: string, Reason: string }]
required property var profileHolds
signal powerManagementChanged(bool disabled)
signal inhibitionChangeRequested(bool inhibit)
signal activateProfileRequested(string profile)
collapseMarginsHint: true
KeyNavigation.down: pmSwitch.pmCheckBox
//footer:
contentItem: PlasmaComponents3.ScrollView {
id: scrollView
focus: false
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
function positionViewAtItem(item) {
if (!PlasmaComponents3.ScrollBar.vertical.visible) {
return;
}
const rect = batteryList.mapFromItem(item, 0, 0, item.width, item.height);
if (rect.y < scrollView.contentItem.contentY) {
scrollView.contentItem.contentY = rect.y;
} else if (rect.y + rect.height > scrollView.contentItem.contentY + scrollView.height) {
scrollView.contentItem.contentY = rect.y + rect.height - scrollView.height;
}
}
Column {
id: batteryList
spacing: Kirigami.Units.smallSpacing
readonly property Item firstHeaderItem: {
if (powerProfileItem.visible) {
return powerProfileItem;
}
return null;
}
readonly property Item lastHeaderItem: {
if (powerProfileItem.visible) {
return powerProfileItem;
}
return null;
}
Repeater {
id: batteryRepeater
delegate: BatteryItem {
width: scrollView.availableWidth
battery: model
remainingTime: dialog.remainingTime
batteryIndex: index
KeyNavigation.up: index === 0 ? batteryList.lastHeaderItem : batteryRepeater.itemAt(index - 1)
KeyNavigation.down: index + 1 < batteryRepeater.count ? batteryRepeater.itemAt(index + 1) : null
KeyNavigation.backtab: KeyNavigation.up
KeyNavigation.tab: KeyNavigation.down
Keys.onTabPressed: event => {
if (index === batteryRepeater.count - 1) {
// Workaround to leave applet's focus on desktop
nextItemInFocusChain(false).forceActiveFocus(Qt.TabFocusReason);
} else {
event.accepted = false;
}
}
onActiveFocusChanged: if (activeFocus) scrollView.positionViewAtItem(this)
}
}
Separator {
width: scrollView.availableWidth
visible: model.count !== 0
}
PowerProfileItem {
id: powerProfileItem
width: scrollView.availableWidth
KeyNavigation.down: batteryRepeater.count > 0 ? batteryRepeater.itemAt(0) : null
KeyNavigation.backtab: KeyNavigation.up
KeyNavigation.tab: KeyNavigation.down
profilesInstalled: dialog.profilesInstalled
profilesAvailable: dialog.profiles.length > 0
activeProfile: dialog.activeProfile
inhibitionReason: dialog.inhibitionReason
degradationReason: dialog.degradationReason
profileHolds: dialog.profileHolds
onActivateProfileRequested: profile => {
dialog.activateProfileRequested(profile);
}
onActiveFocusChanged: if (activeFocus) scrollView.positionViewAtItem(this)
}
PowerManagementItem {
id: pmSwitch
width: scrollView.availableWidth
inhibitions: dialog.inhibitions
manuallyInhibited: dialog.manuallyInhibited
inhibitsLidAction: dialog.inhibitsLidAction
pluggedIn: dialog.pluggedIn
onInhibitionChangeRequested: inhibit => {
dialog.inhibitionChangeRequested(inhibit);
}
onDisabledChanged: dialog.powerManagementChanged(disabled)
}
InhibitionHint {
width: scrollView.availableWidth
visible: batterymonitor.isBroken && typeof batterymonitor.batteryCapacity !== "undefined"
iconSource: "info"
leftPadding: Kirigami.Units.iconSizes.small
text: "There is a problem with your battery, so your computer might shut down suddenly."
}
}
}
}

View file

@ -0,0 +1,140 @@
/*
SPDX-FileCopyrightText: 2012-2013 Daniel Nicoletti <dantti12@gmail.com>
SPDX-FileCopyrightText: 2013, 2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QtControls
import org.kde.kwindowsystem
import org.kde.plasma.components as PlasmaComponents3
import org.kde.ksvg as KSvg
import org.kde.kirigami as Kirigami
ColumnLayout {
id: root
property alias pmCheckBox: pmCheckBox
property alias disabled: pmCheckBox.checked
property bool pluggedIn
signal inhibitionChangeRequested(bool inhibit)
// List of active power management inhibitions (applications that are
// blocking sleep and screen locking).
//
// type: [{
// Icon: string,
// Name: string,
// Reason: string,
// }]
property var inhibitions: []
property bool manuallyInhibited
property bool inhibitsLidAction
// UI to manually inhibit sleep and screen locking
QtControls.CheckBox {
id: pmCheckBox
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing
text: i18nc("Minimize the length of this string as much as possible", "Manually block sleep and screen locking")
checked: root.manuallyInhibited
focus: true
KeyNavigation.up: dialog.KeyNavigation.up
KeyNavigation.down: batteryList.children[0]
KeyNavigation.backtab: dialog.KeyNavigation.backtab
KeyNavigation.tab: KeyNavigation.down
onToggled: {
inhibitionChangeRequested(checked)
}
}
// Separator line
Separator {
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.leftMargin: -Kirigami.Units.largeSpacing - Kirigami.Units.smallSpacing
Layout.rightMargin: -Kirigami.Units.largeSpacing
visible: inhibitionReasonsLayout.visible
}
/*KSvg.SvgItem {
Layout.fillWidth: true
visible: inhibitionReasonsLayout.visible
imagePath: "widgets/line"
elementId: "horizontal-line"
}*/
// list of automatic inhibitions
ColumnLayout {
id: inhibitionReasonsLayout
Layout.fillWidth: true
visible: root.inhibitsLidAction || (root.inhibitions.length > 0)
Layout.leftMargin: Kirigami.Units.iconSizes.small
Layout.rightMargin: Kirigami.Units.iconSizes.small
InhibitionHint {
Layout.fillWidth: true
visible: root.inhibitsLidAction
iconSource: "computer-laptop"
text: i18nc("Minimize the length of this string as much as possible", "Your laptop is configured not to sleep when closing the lid while an external monitor is connected.")
}
PlasmaComponents3.Label {
id: inhibitionExplanation
Layout.fillWidth: true
visible: root.inhibitions.length > 1
//font: Kirigami.Theme.smallFont
wrapMode: Text.WordWrap
elide: Text.ElideRight
maximumLineCount: 3
text: i18np("%1 application is currently blocking sleep and screen locking:",
"%1 applications are currently blocking sleep and screen locking:",
root.inhibitions.length)
}
Repeater {
model: root.inhibitions
InhibitionHint {
property string icon: modelData.Icon
|| (KWindowSystem.isPlatformWayland ? "wayland" : "xorg")
property string name: modelData.Name
property string reason: modelData.Reason
Layout.fillWidth: true
iconSource: icon
text: {
if (root.inhibitions.length === 1) {
if (reason && name) {
return i18n("%1 is currently blocking sleep and screen locking (%2)", name, reason)
} else if (name) {
return i18n("%1 is currently blocking sleep and screen locking (unknown reason)", name)
} else if (reason) {
return i18n("An application is currently blocking sleep and screen locking (%1)", reason)
} else {
return i18n("An application is currently blocking sleep and screen locking (unknown reason)")
}
} else {
if (reason && name) {
return i18nc("Application name: reason for preventing sleep and screen locking", "%1: %2", name, reason)
} else if (name) {
return i18nc("Application name: reason for preventing sleep and screen locking", "%1: unknown reason", name)
} else if (reason) {
return i18nc("Application name: reason for preventing sleep and screen locking", "Unknown application: %1", reason)
} else {
return i18nc("Application name: reason for preventing sleep and screen locking", "Unknown application: unknown reason")
}
}
}
}
}
}
}

View file

@ -0,0 +1,307 @@
/*
* SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
* SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QtControls
import org.kde.plasma.components as PlasmaComponents3
import org.kde.kirigami as Kirigami
PlasmaComponents3.ItemDelegate {
id: root
//property alias slider: slider
property bool profilesInstalled
property bool profilesAvailable
property string activeProfile
property string inhibitionReason
readonly property bool inhibited: inhibitionReason !== ""
property string degradationReason
// type: [{ Name: string, Icon: string, Profile: string, Reason: string }]
required property var profileHolds
// The canBeInhibited property mean that this profile's availability
// depends on root.inhibited value (and thus on the
// inhibitionReason string).
readonly property var profileData: [
{
label: i18n("Power Save"),
profile: "power-saver",
canBeInhibited: false,
}, {
label: i18n("Balanced"),
profile: "balanced",
canBeInhibited: false,
}, {
label: i18n("Performance"),
profile: "performance",
canBeInhibited: true,
}
]
readonly property int activeProfileIndex: profileData.findIndex(data => data.profile === activeProfile)
// type: typeof(profileData[])?
readonly property var activeProfileData: activeProfileIndex !== -1 ? profileData[activeProfileIndex] : undefined
// type: typeof(profileHolds)
readonly property var activeHolds: profileHolds.filter(hold => hold.Profile === activeProfile)
signal activateProfileRequested(string profile)
highlighted: false
down: false
//background.visible: highlighted
//highlighted: activeFocus
//hoverEnabled: false
//text: i18n("Power Profile")
Accessible.description: !root.profilesAvailable ? i18nc("Power profile", "Not available") : activeProfileData ? activeProfileData.label : ""
Accessible.role: Accessible.Slider
//Keys.forwardTo: [slider]
contentItem: RowLayout {
spacing: Kirigami.Units.gridUnit
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 0
/*RowLayout {
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
PlasmaComponents3.Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: root.text
}
PlasmaComponents3.Label {
id: activeProfileLabel
Layout.alignment: Qt.AlignRight
text: !root.profilesAvailable ? i18nc("Power profile", "Not available") : activeProfileData ? activeProfileData.label : ""
enabled: root.profilesAvailable
}
}*/
ColumnLayout {
id: profileConfiguration
visible: root.profilesAvailable
PlasmaComponents3.Label {
text: "Select a power plan:"
opacity: 0.75
}
QtControls.ButtonGroup {
buttons: radioButtons.children
onClicked: (button) => {
const { canBeInhibited, profile } = root.profileData[button.value];
if (!(canBeInhibited && root.inhibited)) {
activateProfileRequested(profile);
}/* else {
value = Qt.binding(() => root.activeProfileIndex);
}*/
}
}
ColumnLayout {
id: radioButtons
QtControls.RadioButton {
id: balancedRadio
property string profileId: "balanced"
property int value: 1
text: root.profileData.find(profile => profile.profile === profileId).label
checked: activeProfileData.profile == profileId
}
QtControls.RadioButton {
id: powerSaverRadio
property string profileId: "power-saver"
property int value: 0
text: root.profileData.find(profile => profile.profile === profileId).label
checked: activeProfileData.profile == profileId
}
QtControls.RadioButton {
id: performanceRadio
property string profileId: "performance"
property int value: 2
text: root.profileData.find(profile => profile.profile === profileId).label
checked: activeProfileData.profile == profileId
}
}
}
/*PlasmaComponents3.Slider {
id: slider
visible: root.profilesAvailable
Layout.fillWidth: true
activeFocusOnTab: false
from: 0
to: 2
stepSize: 1
value: root.activeProfileIndex
snapMode: PlasmaComponents3.Slider.SnapAlways
onMoved: {
}
// fake having a disabled second half
Rectangle {
z: -1
visible: root.inhibited
color: Kirigami.Theme.backgroundColor
anchors {
top: parent.background.top
left: parent.horizontalCenter
leftMargin: 1
right: parent.right
bottom: parent.background.bottom
}
opacity: 0.4
}
}*/
/*RowLayout {
spacing: 0
visible: root.profilesAvailable
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.fillWidth: true
Kirigami.Icon {
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
source: "battery-profile-powersave-symbolic"
HoverHandler {
id: powersaveIconHover
}
PlasmaComponents3.ToolTip {
text: root.profileData.find(profile => profile.profile === "power-saver").label
visible: powersaveIconHover.hovered
}
}
Item {
Layout.fillWidth: true
}
Kirigami.Icon {
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
source: "battery-profile-performance-symbolic"
HoverHandler {
id: performanceIconHover
}
PlasmaComponents3.ToolTip {
text: root.profileData.find(profile => profile.profile === "performance").label
visible: performanceIconHover.hovered
}
}
}*/
// NOTE Only one of these will be visible at a time since the daemon will only set one depending
// on its version
InhibitionHint {
id: inhibitionReasonHint
Layout.fillWidth: true
visible: root.inhibited
iconSource: "dialog-information"
text: switch(root.inhibitionReason) {
case "lap-detected":
return i18n("Performance mode has been disabled to reduce heat generation because the computer has detected that it may be sitting on your lap.")
case "high-operating-temperature":
return i18n("Performance mode is unavailable because the computer is running too hot.")
default:
return i18n("Performance mode is unavailable.")
}
}
InhibitionHint {
id: inhibitionPerformanceHint
Layout.fillWidth: true
visible: root.activeProfile === "performance" && root.degradationReason !== ""
iconSource: "dialog-information"
text: switch(root.degradationReason) {
case "lap-detected":
return i18n("Performance may be lowered to reduce heat generation because the computer has detected that it may be sitting on your lap.")
case "high-operating-temperature":
return i18n("Performance may be reduced because the computer is running too hot.")
default:
return i18n("Performance may be reduced.")
}
}
InhibitionHint {
id: inhibitionHoldersHint
Layout.fillWidth: true
visible: root.activeHolds.length > 0 && root.activeProfileData !== undefined
text: root.activeProfileData !== undefined
? i18np("One application has requested activating %2:",
"%1 applications have requested activating %2:",
root.activeHolds.length,
i18n(root.activeProfileData.label))
: ""
}
Repeater {
id: repeater
model: root.activeHolds
InhibitionHint {
Layout.fillWidth: true
x: Kirigami.Units.smallSpacing
iconSource: modelData.Icon
text: i18nc("%1 is the name of the application, %2 is the reason provided by it for activating performance mode",
"%1: %2", modelData.Name, modelData.Reason)
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.smallSpacing
visible: repeater.visibleChildren > 0
|| inhibitionReasonHint.visible
|| inhibitionPerformanceHint.visible
|| inhibitionHoldersHint.visible
}
RowLayout {
visible: !root.profilesInstalled
spacing: Kirigami.Units.smallSpacing
PlasmaComponents3.Label {
text: xi18n("Power profiles may be supported on your device.<nl/>Try installing the <command>power-profiles-daemon</command> package using your distribution's package manager and restarting the system.")
enabled: false
font: Kirigami.Theme.smallFont
wrapMode: Text.Wrap
Layout.fillWidth: true
}
}
}
}
}

View file

@ -0,0 +1,8 @@
import QtQuick 2.0
import QtQuick.Layouts
Rectangle {
id: separator
height: 1
color: "#dde0e2"
}

View file

@ -0,0 +1,56 @@
/*
SPDX-FileCopyrightText: 2011 Sebastian Kügler <sebas@kde.org>
SPDX-FileCopyrightText: 2012 Viranch Mehta <viranch.mehta@gmail.com>
SPDX-FileCopyrightText: 2014-2016 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
function stringForBatteryState(batteryData, source) {
if (batteryData["Plugged in"]) {
// When we are using a charge threshold, the kernel
// may stop charging within a percentage point of the actual threshold
// and this is considered correct behavior, so we have to handle
// that. See https://bugzilla.kernel.org/show_bug.cgi?id=215531.
if (typeof source.data["Battery"]["Charge Stop Threshold"] === "number"
&& (batteryData.Percent >= source.data["Battery"]["Charge Stop Threshold"] - 1
&& batteryData.Percent <= source.data["Battery"]["Charge Stop Threshold"] + 1)
// Also, Upower may give us a status of "Not charging" rather than
// "Fully charged", so we need to account for that as well. See
// https://gitlab.freedesktop.org/upower/upower/-/issues/142.
&& (batteryData.State === "NoCharge" || batteryData.State === "FullyCharged")
&& batteryData["Is Power Supply"]
) {
return i18n("Fully Charged");
}
// Otherwise, just look at the charge state
switch(batteryData["State"]) {
case "Discharging": return i18n("Discharging");
case "FullyCharged": return i18n("Fully Charged");
case "Charging": return i18n("Charging");
// when in doubt we're not charging
default: return i18n("Not Charging");
}
} else {
return i18nc("Battery is currently not present in the bay", "Not present");
}
}
function updateInhibitions(rootItem, source) {
const inhibitions = [];
const manualInhibitions = [];
if (source.data["Inhibitions"]) {
for (let key in pmSource.data["Inhibitions"]) {
if (key === "plasmashell" || key === "plasmoidviewer") {
manualInhibitions.push(key);
} else {
inhibitions.push(pmSource.data["Inhibitions"][key]);
}
}
}
rootItem.manuallyInhibited = manualInhibitions.length > 0;
rootItem.inhibitions = inhibitions;
}

View file

@ -0,0 +1,349 @@
/*
SPDX-FileCopyrightText: 2011 Sebastian Kügler <sebas@kde.org>
SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
SPDX-FileCopyrightText: 2013-2015 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-FileCopyrightText: 2021-2022 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import org.kde.coreaddons as KCoreAddons
import org.kde.kcmutils // KCMLauncher
import org.kde.config // KAuthorized
import org.kde.notification
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasma5support as P5Support
import org.kde.plasma.plasmoid
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels as KItemModels
import "logic.js" as Logic
PlasmoidItem {
id: batterymonitor
property QtObject pmSource: P5Support.DataSource {
id: pmSource
engine: "powermanagement"
connectedSources: sources
onSourceAdded: source => {
disconnectSource(source);
connectSource(source);
}
onSourceRemoved: source => {
disconnectSource(source);
}
onDataChanged: {
Logic.updateInhibitions(batterymonitor, pmSource);
}
}
property QtObject batteries: KItemModels.KSortFilterProxyModel {
id: batteries
filterRoleName: "Is Power Supply"
sortOrder: Qt.DescendingOrder
sourceModel: KItemModels.KSortFilterProxyModel {
sortRoleName: "Pretty Name"
sortOrder: Qt.AscendingOrder
sortCaseSensitivity: Qt.CaseInsensitive
sourceModel: P5Support.DataModel {
dataSource: pmSource
sourceFilter: "Battery[0-9]+"
}
}
}
property int batteryCapacity: batteries.sourceModel.sourceModel.dataSource.data["Battery0"] !== undefined ? batteries.sourceModel.sourceModel.dataSource.data["Battery0"].Capacity : 0
readonly property bool isBroken: batteryCapacity > 0 && batteryCapacity < 50
readonly property bool hasBatteries: batteries.count > 0 && pmSource.data["Battery"]["Has Cumulative"]
readonly property bool kcmAuthorized: KAuthorized.authorizeControlModule("powerdevilprofilesconfig")
readonly property bool kcmEnergyInformationAuthorized: KAuthorized.authorizeControlModule("kcm_energyinfo")
readonly property bool isPluggedIn: pmSource.data["AC Adapter"]["Plugged in"]
readonly property bool isSomehowFullyCharged: (pmSource.data["AC Adapter"]["Plugged in"] && pmSource.data["Battery"]["State"] === "FullyCharged") ||
// When we are using a charge threshold, the kernel
// may stop charging within a percentage point of the actual threshold
// and this is considered correct behavior, so we have to handle
// that. See https://bugzilla.kernel.org/show_bug.cgi?id=215531.
(pmSource.data["AC Adapter"]["Plugged in"]
&& typeof pmSource.data["Battery"]["Charge Stop Threshold"] === "number"
&& (pmSource.data.Battery.Percent >= pmSource.data["Battery"]["Charge Stop Threshold"] - 1
&& pmSource.data.Battery.Percent <= pmSource.data["Battery"]["Charge Stop Threshold"] + 1)
// Also, Upower may give us a status of "Not charging" rather than
// "Fully charged", so we need to account for that as well. See
// https://gitlab.freedesktop.org/upower/upower/-/issues/142.
&& (pmSource.data["Battery"]["State"] === "NoCharge" || pmSource.data["Battery"]["State"] === "FullyCharged"))
readonly property int remainingTime: Number(pmSource.data["Battery"]["Smoothed Remaining msec"])
readonly property var profiles: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Profiles"] || []) : []
property bool isManuallyInPerformanceMode: false // to be set on power profile requested through the applet
property bool isManuallyInPowerSaveMode: false // to be set on power profile requested through the applet
readonly property bool isSomehowInPerformanceMode: actuallyActiveProfile === "performance"// Don't care about whether it was manually one or due to holds
readonly property bool isSomehowInPowerSaveMode: actuallyActiveProfile === "power-saver" // Don't care about whether it was manually one or due to holds
readonly property bool isHeldOnPerformanceMode: isSomehowInPerformanceMode && activeProfileHolds.length > 0
readonly property bool isHeldOnPowerSaveMode: isSomehowInPowerSaveMode && activeProfileHolds.length > 0
readonly property bool inPanel: (Plasmoid.location === PlasmaCore.Types.TopEdge
|| Plasmoid.location === PlasmaCore.Types.RightEdge
|| Plasmoid.location === PlasmaCore.Types.BottomEdge
|| Plasmoid.location === PlasmaCore.Types.LeftEdge)
property bool powermanagementDisabled: false
// List of active power management inhibitions (applications that are
// blocking sleep and screen locking).
//
// type: [{
// Icon: string,
// Name: string,
// Reason: string,
// }]
property var inhibitions: []
property bool manuallyInhibited: false
readonly property var activeProfileHolds: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Profile Holds"] || []) : []
readonly property string actuallyActiveProfile: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Current Profile"] || "") : ""
function symbolicizeIconName(iconName) {
const symbolicSuffix = "-symbolic";
if (iconName.endsWith(symbolicSuffix)) {
return iconName;
}
return iconName + symbolicSuffix;
}
switchWidth: Kirigami.Units.gridUnit * 10
switchHeight: Kirigami.Units.gridUnit * 10
Plasmoid.title: "More power options"//hasBatteries ? i18n("Power and Battery") : i18n("Power Management")
LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
LayoutMirroring.childrenInherit: true
Plasmoid.status: {
if (powermanagementDisabled) {
return PlasmaCore.Types.ActiveStatus;
}
if (pmSource.data.Battery["Has Cumulative"] && pmSource.data["Battery"]["State"] === "Discharging") {
return PlasmaCore.Types.ActiveStatus;
}
if (isManuallyInPerformanceMode || isManuallyInPowerSaveMode || isHeldOnPerformanceMode || isHeldOnPowerSaveMode) {
return PlasmaCore.Types.ActiveStatus;
}
return PlasmaCore.Types.PassiveStatus;
}
toolTipMainText: {
if (!hasBatteries) {
return Plasmoid.title
} else if (isSomehowFullyCharged) {
return i18n("Fully Charged");
}
const percent = pmSource.data.Battery.Percent;
if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"]) {
const state = pmSource.data.Battery.State;
if (state === "NoCharge") {
return i18n("Battery at %1%, not Charging", percent);
} else if (state === "Discharging") {
return i18n("Battery at %1%, plugged in but still discharging", percent);
} else if (state === "Charging") {
return i18n("Battery at %1%, Charging", percent);
}
}
return i18n("Battery at %1%", percent);
}
toolTipSubText: {
const parts = [];
// Add special text for the "plugged in but still discharging" case
if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"] && pmSource.data.Battery.State === "Discharging") {
parts.push(i18n("The power supply is not powerful enough to charge the battery"));
}
if (batteries.count === 0) {
parts.push(i18n("No Batteries Available"));
} else if (remainingTime > 0) {
const remainingTimeString = KCoreAddons.Format.formatDuration(remainingTime, KCoreAddons.FormatTypes.HideSeconds);
if (pmSource.data["Battery"]["State"] === "FullyCharged") {
// Don't add anything
} else if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"] && pmSource.data.Battery.State === "Charging") {
parts.push(i18nc("time until fully charged - HH:MM","%1 until fully charged", remainingTimeString));
} else {
parts.push(i18nc("remaining time left of battery usage - HH:MM","%1 remaining", remainingTimeString));
}
} else if (pmSource.data.Battery.State === "NoCharge" && !isSomehowFullyCharged) {
parts.push(i18n("Not charging"));
} // otherwise, don't add anything
if (powermanagementDisabled) {
parts.push(i18n("Automatic sleep and screen locking are disabled"));
}
if (isSomehowInPerformanceMode) {
if (isHeldOnPerformanceMode) {
parts.push(i18np("An application has requested activating Performance mode",
"%1 applications have requested activating Performance mode",
activeProfileHolds.length));
} else {
parts.push(i18n("System is in Performance mode"));
}
} else if (isSomehowInPowerSaveMode) {
if (isHeldOnPowerSaveMode) {
parts.push(i18np("An application has requested activating Power Save mode",
"%1 applications have requested activating Power Save mode",
activeProfileHolds.length));
} else {
parts.push(i18n("System is in Power Save mode"));
}
}
return parts.join("\n");
}
Plasmoid.icon: {
let iconName;
if (hasBatteries) {
iconName = "battery-full";
} else {
iconName = "battery-profile-performance";
}
if (inPanel) {
return symbolicizeIconName(iconName);
}
return iconName;
}
compactRepresentation: CompactRepresentation {
hasBatteries: batterymonitor.hasBatteries
batteries: batterymonitor.batteries
isSetToPerformanceMode: batterymonitor.isHeldOnPerformanceMode || batterymonitor.isManuallyInPerformanceMode
isSetToPowerSaveMode: batterymonitor.isHeldOnPowerSaveMode || batterymonitor.isManuallyInPowerSaveMode
isSomehowFullyCharged: batterymonitor.isSomehowFullyCharged
}
fullRepresentation: PopupDialog {
id: dialogItem
readonly property var appletInterface: batterymonitor
readonly property int flyoutIntendedWidth: Kirigami.Units.gridUnit * 15
Layout.minimumWidth: Kirigami.Units.gridUnit * 10
Layout.maximumWidth: Kirigami.Units.gridUnit * 80
Layout.preferredWidth: Kirigami.Units.gridUnit * 20
Layout.minimumHeight: Kirigami.Units.gridUnit * 10
Layout.maximumHeight: Kirigami.Units.gridUnit * 40
Layout.preferredHeight: implicitHeight
model: batteries
pluggedIn: pmSource.data["AC Adapter"] !== undefined && pmSource.data["AC Adapter"]["Plugged in"]
remainingTime: batterymonitor.remainingTime
activeProfile: batterymonitor.actuallyActiveProfile
inhibitions: batterymonitor.inhibitions
manuallyInhibited: batterymonitor.manuallyInhibited
inhibitsLidAction: pmSource.data["PowerDevil"] && pmSource.data["PowerDevil"]["Is Lid Present"] && !pmSource.data["PowerDevil"]["Triggers Lid Action"] ? true : false
profilesInstalled: pmSource.data["Power Profiles"] ? pmSource.data["Power Profiles"]["Installed"] : false
profiles: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Profiles"] || []) : []
inhibitionReason: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Performance Inhibited Reason"] || "") : ""
degradationReason: pmSource.data["Power Profiles"] ? (pmSource.data["Power Profiles"]["Performance Degraded Reason"] || "") : ""
profileHolds: batterymonitor.activeProfileHolds
onInhibitionChangeRequested: inhibit => {
const service = pmSource.serviceForSource("PowerDevil");
if (inhibit) {
const reason = i18n("The battery applet has enabled system-wide inhibition");
const op1 = service.operationDescription("beginSuppressingSleep");
op1.reason = reason;
const op2 = service.operationDescription("beginSuppressingScreenPowerManagement");
op2.reason = reason;
const job1 = service.startOperationCall(op1);
const job2 = service.startOperationCall(op2);
} else {
const op1 = service.operationDescription("stopSuppressingSleep");
const op2 = service.operationDescription("stopSuppressingScreenPowerManagement");
const job1 = service.startOperationCall(op1);
const job2 = service.startOperationCall(op2);
}
Logic.updateInhibitions(batterymonitor, pmSource);
}
onPowerManagementChanged: disabled => {
batterymonitor.powermanagementDisabled = disabled
}
Notification {
id: powerProfileError
componentName: "plasma_workspace"
eventId: "warning"
iconName: "speedometer"
title: i18n("Power Management")
}
onActivateProfileRequested: profile => {
dialogItem.activeProfile = profile;
const service = pmSource.serviceForSource("PowerDevil");
const op = service.operationDescription("setPowerProfile");
op.profile = profile;
const job = service.startOperationCall(op);
job.finished.connect(job => {
dialogItem.activeProfile = Qt.binding(() => actuallyActiveProfile);
if (!job.result) {
powerProfileError.text = i18n("Failed to activate %1 mode", profile);
powerProfileError.sendEvent();
return;
}
batterymonitor.isManuallyInPerformanceMode = profile == "performance";
batterymonitor.isManuallyInPowerSaveMode = profile == "power-saver";
});
}
}
Plasmoid.contextualActions: [
PlasmaCore.Action {
text: i18n("&Show Energy Information…")
icon.name: "documentinfo"
visible: batterymonitor.kcmEnergyInformationAuthorized
onTriggered: KCMLauncher.openInfoCenter("kcm_energyinfo")
}/*,
PlasmaCore.Action {
text: i18n("Show Battery Percentage on Icon When Not Fully Charged")
icon.name: "format-number-percent"
visible: batterymonitor.hasBatteries
checkable: true
checked: Plasmoid.configuration.showPercentage
onTriggered: checked => {
Plasmoid.configuration.showPercentage = checked
}
}*/
]
PlasmaCore.Action {
id: configureAction
text: i18n("&Configure Energy Saving…")
icon.name: "configure"
shortcut: "alt+d, s"
onTriggered: {
KCMLauncher.openSystemSettings("kcm_powerdevilprofilesconfig");
}
}
Component.onCompleted: {
Logic.updateInhibitions(batterymonitor, pmSource)
Plasmoid.setInternalAction("configure", configureAction);
}
}