Personal noctalia plugins collection
1import QtQuick
2import Quickshell
3import Quickshell.Services.UPower
4import qs.Commons
5import qs.Widgets
6import qs.Modules.Bar.Extras
7import qs.Services.Hardware
8import qs.Services.UI
9
10Item {
11 id: root
12
13 property QtObject pluginApi: null
14 readonly property QtObject pluginCore: pluginApi?.mainInstance
15
16 property ShellScreen screen
17 property string widgetId: ""
18 property string section: ""
19 property int sectionWidgetIndex: -1
20 property int sectionWidgetsCount: 0
21
22 // Battery state from BatteryService
23 readonly property var selectedDevice: BatteryService.displayDevice
24 readonly property bool isReady: BatteryService.isDeviceReady(selectedDevice)
25 readonly property real percent: isReady ? BatteryService.getPercentage(selectedDevice) : -1
26 readonly property bool isCharging: isReady ? BatteryService.isCharging(selectedDevice) : false
27 readonly property bool isPluggedIn: isReady ? BatteryService.isPluggedIn(selectedDevice) : false
28 readonly property bool isLowBattery: isReady ? BatteryService.isLowBattery(selectedDevice) : false
29 readonly property bool isCriticalBattery: isReady ? BatteryService.isCriticalBattery(selectedDevice) : false
30
31 readonly property var tooltipContent: {
32 let rows = [];
33 if (isReady) {
34 rows.push(["Battery", percent + "%"]);
35
36 let timeText = BatteryService.getTimeRemainingText(selectedDevice);
37 if (timeText) {
38 const idx = timeText.indexOf(":");
39 if (idx >= 0)
40 rows.push([timeText.substring(0, idx).trim(), timeText.substring(idx + 1).trim()]);
41 else
42 rows.push([timeText, ""]);
43 }
44
45 let rateText = BatteryService.getRateText(selectedDevice);
46 if (rateText) {
47 const idx = rateText.indexOf(":");
48 if (idx >= 0)
49 rows.push([rateText.substring(0, idx).trim(), rateText.substring(idx + 1).trim()]);
50 else
51 rows.push([rateText, ""]);
52 }
53 }
54
55 if (pluginCore) {
56 if (rows.length > 0) rows.push(["---", "---"]);
57 rows.push(["Charge limit", pluginCore.chargeLimit + "%"]);
58 }
59
60 return rows;
61 }
62
63 implicitWidth: pill.width
64 implicitHeight: pill.height
65
66 BarPill {
67 id: pill
68 screen: root.screen
69 oppositeDirection: BarService.getPillDirection(root)
70 forceClose: false
71
72 icon: BatteryService.getIcon(root.percent, root.isCharging, root.isPluggedIn, root.isReady)
73 text: root.isReady ? root.percent : "-"
74 suffix: pluginCore && pluginCore.chargeLimit < 100 ? "% \u2193" + pluginCore.chargeLimit : "%"
75 autoHide: false
76 customBackgroundColor: root.isCharging ? Color.mPrimary : ((root.isLowBattery || root.isCriticalBattery) ? Color.mError : "transparent")
77 customTextIconColor: root.isCharging ? Color.mOnPrimary : ((root.isLowBattery || root.isCriticalBattery) ? Color.mOnError : "transparent")
78 tooltipText: root.tooltipContent
79
80 onClicked: pluginCore?.toggle()
81
82 onRightClicked: {
83 PanelService.showContextMenu(contextMenu, pill, root.screen);
84 }
85 }
86
87 NPopupContextMenu {
88 id: contextMenu
89 model: [
90 {
91 "label": pluginCore && pluginCore.chargeLimit < 100
92 ? "Set charge limit to 100%"
93 : "Set charge limit to 80%",
94 "action": "toggle",
95 "icon": "battery_saver",
96 "enabled": true
97 }
98 ]
99
100 onTriggered: action => {
101 contextMenu.close();
102 PanelService.closeContextMenu(root.screen);
103
104 if (action === "toggle") {
105 pluginCore?.toggle();
106 }
107 }
108 }
109}