diff --git a/Assets/Translations/en-GB.json b/Assets/Translations/en-GB.json index 38ed26c1e2..a1c92ff67f 100644 --- a/Assets/Translations/en-GB.json +++ b/Assets/Translations/en-GB.json @@ -58,6 +58,7 @@ "display-mode-icon-always": "Icon - Always show %", "display-mode-icon-hover": "Icon - Show on hover", "display-mode-icon-only": "Icon only", + "display-mode-android-16": "Android 16 style", "hide-if-idle-description": "Hide the widget when the battery is not charging or discharging.", "hide-if-idle-label": "Hide when idle", "hide-if-not-detected-description": "Hide the widget when no battery is detected on the system.", diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 24dca05a72..2628d31193 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -58,6 +58,7 @@ "display-mode-icon-always": "Icon - Always show %", "display-mode-icon-hover": "Icon - Show on hover", "display-mode-icon-only": "Icon only", + "display-mode-android-16": "Android 16 style", "hide-if-idle-description": "Hide the widget when the battery is not charging or discharging.", "hide-if-idle-label": "Hide when idle", "hide-if-not-detected-description": "Hide the widget when no battery is detected on the system.", diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index 12f197ddd9..06271dca62 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell +import Quickshell.Io import Quickshell.Wayland import Quickshell.Widgets import qs.Commons @@ -59,6 +60,9 @@ Item { readonly property string windowTitle: CompositorService.getFocusedWindowTitle() || "No active window" readonly property string fallbackIcon: "user-desktop" + // Memory usage tooltip + property string memoryUsage: "" + readonly property int iconSize: Style.toOdd(capsuleHeight * 0.75) readonly property int verticalSize: Style.toOdd(capsuleHeight * 0.85) @@ -334,6 +338,41 @@ Item { } } + // Process to query active window PID and memory usage + Process { + id: memoryQueryProcess + running: false + command: ["sh", "-c", "PID=$(hyprctl activewindow -j 2>/dev/null | grep '\"pid\":' | head -1 | sed 's/[^0-9]//g'); if [ -n \"$PID\" ]; then pstree -p $PID 2>/dev/null | tr '(' '\\n' | grep -oE '^[0-9]+' | xargs ps -o rss= -p 2>/dev/null | awk '{sum+=$1} END {if(sum>0) print int(sum)}'; fi"] + + property string outputLine: "" + + stdout: SplitParser { + onRead: function(line) { + memoryQueryProcess.outputLine = line.trim(); + } + } + + onExited: function(exitCode) { + var kbStr = outputLine; + outputLine = ""; + if (kbStr && !isNaN(kbStr)) { + var kb = parseInt(kbStr); + if (kb >= 1024) { + root.memoryUsage = Math.round(kb / 1024) + " MB"; + } else { + root.memoryUsage = kb + " kB"; + } + // Re-show tooltip with updated content (show() handles same-target update) + if (mainMouseArea.containsMouse && root.memoryUsage !== "") { + var content = ((windowTitle !== "") && isVerticalBar || (scrollingMode === "never")) + ? windowTitle + "\nMemory: " + root.memoryUsage + : "Memory: " + root.memoryUsage; + TooltipService.show(root, content, BarService.getTooltipDirection(root.screen?.name)); + } + } + } + } + // Mouse area for hover detection MouseArea { id: mainMouseArea @@ -349,11 +388,19 @@ Item { cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton onEntered: { - if ((windowTitle !== "") && isVerticalBar || (scrollingMode === "never")) { - TooltipService.show(root, windowTitle, BarService.getTooltipDirection(root.screen?.name)); + root.memoryUsage = ""; + if (hasFocusedWindow) { + if ((windowTitle !== "") && isVerticalBar || (scrollingMode === "never")) { + TooltipService.show(root, windowTitle + "\nMemory: ...", BarService.getTooltipDirection(root.screen?.name)); + } else { + TooltipService.show(root, "Memory: ...", BarService.getTooltipDirection(root.screen?.name)); + } + // Trigger memory query + memoryQueryProcess.running = true; } } onExited: { + root.memoryUsage = ""; TooltipService.hide(); } onClicked: mouse => { diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml index 9e87cf8ee4..09b110e30c 100644 --- a/Modules/Bar/Widgets/Battery.qml +++ b/Modules/Bar/Widgets/Battery.qml @@ -7,6 +7,7 @@ import qs.Commons import qs.Modules.Bar.Extras import qs.Services.Hardware import qs.Services.Networking +import qs.Services.Noctalia import qs.Services.UI import qs.Widgets @@ -39,7 +40,9 @@ Item { readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName) readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode - readonly property bool useGraphicMode: displayMode === "graphic" || displayMode === "graphic-clean" + readonly property bool useGraphicMode: displayMode === "graphic" || displayMode === "graphic-clean" || (displayMode === "android-16" && !pluginExists) + readonly property bool pluginExists: PluginRegistry.isPluginDownloaded("custom-battery") + readonly property bool usePluginMode: displayMode === "android-16" && pluginExists readonly property bool hideIfNotDetected: widgetSettings.hideIfNotDetected !== undefined ? widgetSettings.hideIfNotDetected : widgetMetadata.hideIfNotDetected readonly property bool hideIfIdle: widgetSettings.hideIfIdle !== undefined ? widgetSettings.hideIfIdle : widgetMetadata.hideIfIdle @@ -122,8 +125,8 @@ Item { visible: shouldShow opacity: shouldShow ? 1.0 : 0.0 - implicitWidth: useGraphicMode ? capsule.width : pill.width - implicitHeight: useGraphicMode ? capsule.height : pill.height + implicitWidth: usePluginMode ? (pluginLoader.item ? pluginLoader.item.implicitWidth : 0) : (useGraphicMode ? capsule.width : pill.width) + implicitHeight: usePluginMode ? (pluginLoader.item ? pluginLoader.item.implicitHeight : 0) : (useGraphicMode ? capsule.height : pill.height) NPopupContextMenu { id: contextMenu @@ -151,7 +154,7 @@ Item { // Capsule background (graphic mode only) Rectangle { id: capsule - visible: root.useGraphicMode + visible: root.useGraphicMode && !root.usePluginMode anchors.centerIn: nBattery width: root.isBarVertical ? root.capsuleHeight : nBattery.width + Style.margin2S height: root.isBarVertical ? nBattery.height + Style.margin2S : root.capsuleHeight @@ -171,7 +174,7 @@ Item { NBattery { id: nBattery - visible: root.useGraphicMode + visible: root.useGraphicMode && !root.usePluginMode anchors.centerIn: parent baseSize: (Style.getBarHeightForScreen(root.screenName) / root.capsuleHeight) * Style.fontSizeXXS showPercentageText: root.displayMode !== "graphic-clean" @@ -188,7 +191,7 @@ Item { MouseArea { id: graphicMouseArea - visible: root.useGraphicMode + visible: root.useGraphicMode && !root.usePluginMode anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton @@ -228,7 +231,7 @@ Item { BarPill { id: pill - visible: !root.useGraphicMode + visible: !root.useGraphicMode && !root.usePluginMode screen: root.screen oppositeDirection: BarService.getPillDirection(root) icon: BatteryService.getIcon(root.percent, root.isCharging, root.isPluggedIn, root.isReady) @@ -244,6 +247,38 @@ Item { onRightClicked: PanelService.showContextMenu(contextMenu, pill, screen) } + // ==================== PLUGIN MODE ==================== + + Loader { + id: pluginLoader + active: root.usePluginMode + visible: active + source: active ? "file://" + PluginRegistry.getPluginDir("custom-battery") + "/BarWidget.qml" : "" + + onLoaded: { + item.screen = root.screen; + item.widgetId = root.widgetId; + item.section = root.section; + item.sectionWidgetIndex = root.sectionWidgetIndex; + item.sectionWidgetsCount = root.sectionWidgetsCount; + item.pluginApi = PluginService.loadedPlugins["custom-battery"] ? PluginService.loadedPlugins["custom-battery"].api : null; + } + + anchors.fill: parent + } + + MouseArea { + id: pluginMouseArea + anchors.fill: parent + visible: root.usePluginMode + acceptedButtons: Qt.RightButton + onClicked: mouse => { + if (mouse.button === Qt.RightButton) { + PanelService.showContextMenu(contextMenu, pluginLoader.item || this, screen); + } + } + } + // ==================== SHARED ==================== function getBatteryPanel() { diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml index 7c59cd26fc..d99acd40b5 100644 --- a/Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml +++ b/Modules/Panels/Settings/Bar/WidgetSettings/BatterySettings.qml @@ -3,6 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import qs.Commons import qs.Services.Hardware +import qs.Services.Noctalia import qs.Widgets ColumnLayout { @@ -24,6 +25,21 @@ ColumnLayout { property bool valueHideIfNotDetected: widgetData.hideIfNotDetected !== undefined ? widgetData.hideIfNotDetected : widgetMetadata.hideIfNotDetected property bool valueHideIfIdle: widgetData.hideIfIdle !== undefined ? widgetData.hideIfIdle : widgetMetadata.hideIfIdle + readonly property var displayModeModel: { + var m = [ + { "key": "graphic", "name": I18n.tr("bar.battery.display-mode-graphic") }, + { "key": "graphic-clean", "name": I18n.tr("bar.battery.display-mode-graphic-clean") }, + { "key": "icon-hover", "name": I18n.tr("bar.battery.display-mode-icon-hover") }, + { "key": "icon-always", "name": I18n.tr("bar.battery.display-mode-icon-always") }, + { "key": "icon-only", "name": I18n.tr("bar.battery.display-mode-icon-only") } + ]; + + if (PluginRegistry.isPluginDownloaded("custom-battery")) { + m.push({ "key": "android-16", "name": I18n.tr("bar.battery.display-mode-android-16") }); + } + return m; + } + function saveSettings() { var settings = Object.assign({}, widgetData || {}); if (widgetData && widgetData.id) { @@ -58,28 +74,7 @@ ColumnLayout { label: I18n.tr("common.display-mode") description: I18n.tr("bar.battery.display-mode-description") minimumWidth: 240 - model: [ - { - "key": "graphic", - "name": I18n.tr("bar.battery.display-mode-graphic") - }, - { - "key": "graphic-clean", - "name": I18n.tr("bar.battery.display-mode-graphic-clean") - }, - { - "key": "icon-hover", - "name": I18n.tr("bar.battery.display-mode-icon-hover") - }, - { - "key": "icon-always", - "name": I18n.tr("bar.battery.display-mode-icon-always") - }, - { - "key": "icon-only", - "name": I18n.tr("bar.battery.display-mode-icon-only") - } - ] + model: root.displayModeModel currentKey: root.valueDisplayMode onSelected: key => { root.valueDisplayMode = key;