From aa0f7bb5ef3fe2af87546b1f26e5516e1364cba2 Mon Sep 17 00:00:00 2001 From: Damocles Date: Thu, 23 Apr 2026 00:48:07 +0200 Subject: [PATCH] extract PinnableSection base component from 12 bar modules --- shell/modules/BatteryModule.qml | 22 ++------------------ shell/modules/BluetoothModule.qml | 21 ++----------------- shell/modules/ClockModule.qml | 21 ++----------------- shell/modules/CpuModule.qml | 21 ++----------------- shell/modules/DiskModule.qml | 21 ++----------------- shell/modules/GpuModule.qml | 21 ++----------------- shell/modules/MemoryModule.qml | 21 ++----------------- shell/modules/MprisModule.qml | 21 ++----------------- shell/modules/NetworkModule.qml | 21 ++----------------- shell/modules/NotificationsModule.qml | 21 ++----------------- shell/modules/PinnableSection.qml | 30 +++++++++++++++++++++++++++ shell/modules/TemperatureModule.qml | 21 ++----------------- shell/modules/WeatherModule.qml | 21 ++----------------- shell/modules/qmldir | 1 + 14 files changed, 55 insertions(+), 229 deletions(-) create mode 100644 shell/modules/PinnableSection.qml diff --git a/shell/modules/BatteryModule.qml b/shell/modules/BatteryModule.qml index 7368682..9050f3e 100644 --- a/shell/modules/BatteryModule.qml +++ b/shell/modules/BatteryModule.qml @@ -4,12 +4,12 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing opacity: S.Modules.battery.enable && S.BatteryService.available ? 1 : 0 visible: opacity > 0 - tooltip: "" + _panelHovered: hoverPanel.panelHovered property real _blinkOpacity: 1 @@ -34,24 +34,6 @@ M.BarSection { root._blinkOpacity = 1 } - // Panel state - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - // Bar widgets M.BarIcon { icon: { diff --git a/shell/modules/BluetoothModule.qml b/shell/modules/BluetoothModule.qml index 6cf34e0..91b0bc5 100644 --- a/shell/modules/BluetoothModule.qml +++ b/shell/modules/BluetoothModule.qml @@ -4,29 +4,12 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing opacity: S.Modules.bluetooth.enable && S.BluetoothService.state !== "unavailable" ? 1 : 0 visible: opacity > 0 - tooltip: "" - - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } + _panelHovered: hoverPanel.panelHovered M.BarIcon { icon: "\uF294" diff --git a/shell/modules/ClockModule.qml b/shell/modules/ClockModule.qml index fa06723..69e3663 100644 --- a/shell/modules/ClockModule.qml +++ b/shell/modules/ClockModule.qml @@ -4,33 +4,16 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing - tooltip: "" + _panelHovered: hoverPanel.panelHovered SystemClock { id: clock precision: SystemClock.Seconds } - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarLabel { font.pixelSize: S.Theme.fontSize + 1 label: Qt.formatDateTime(clock.date, "ddd, dd. MMM HH:mm") diff --git a/shell/modules/CpuModule.qml b/shell/modules/CpuModule.qml index 825be3f..16e5db6 100644 --- a/shell/modules/CpuModule.qml +++ b/shell/modules/CpuModule.qml @@ -4,19 +4,15 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: Math.max(1, S.Theme.moduleSpacing - 2) - tooltip: "" + _panelHovered: hoverPanel.panelHovered readonly property var _cores: S.SystemStats.cpuCores readonly property var _coreMaxFreq: S.SystemStats.cpuCoreMaxFreq readonly property var _coreTypes: S.SystemStats.cpuCoreTypes - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - property bool _coreConsumerActive: false on_ShowPanelChanged: { @@ -35,19 +31,6 @@ M.BarSection { onProcessesChanged: hoverPanel.keepOpen(300) } - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: "\uF2DB" anchors.verticalCenter: parent.verticalCenter diff --git a/shell/modules/DiskModule.qml b/shell/modules/DiskModule.qml index 425ce38..616ed65 100644 --- a/shell/modules/DiskModule.qml +++ b/shell/modules/DiskModule.qml @@ -4,10 +4,10 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: Math.max(1, S.Theme.moduleSpacing - 2) - tooltip: "" + _panelHovered: hoverPanel.panelHovered property var _mounts: S.SystemStats.diskMounts property int _rootPct: S.SystemStats.diskRootPct @@ -19,23 +19,6 @@ M.BarSection { return false; } - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: "\uF0C9" color: root._anyWarn ? S.Theme.base09 : root.accentColor diff --git a/shell/modules/GpuModule.qml b/shell/modules/GpuModule.qml index 106463b..794ad55 100644 --- a/shell/modules/GpuModule.qml +++ b/shell/modules/GpuModule.qml @@ -4,28 +4,11 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: Math.max(1, S.Theme.moduleSpacing - 2) - tooltip: "" visible: S.Modules.gpu.enable && S.SystemStats.gpuAvailable - - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } + _panelHovered: hoverPanel.panelHovered M.BarIcon { icon: "\uEB4C" diff --git a/shell/modules/MemoryModule.qml b/shell/modules/MemoryModule.qml index 64f5eff..d1d54bc 100644 --- a/shell/modules/MemoryModule.qml +++ b/shell/modules/MemoryModule.qml @@ -4,10 +4,10 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: Math.max(1, S.Theme.moduleSpacing - 2) - tooltip: "" + _panelHovered: hoverPanel.panelHovered property int percent: S.SystemStats.memPercent property real usedGb: S.SystemStats.memUsedGb @@ -16,29 +16,12 @@ M.BarSection { property real cachedGb: S.SystemStats.memCachedGb property real buffersGb: S.SystemStats.memBuffersGb - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - property M.ProcessList _procs: M.ProcessList { sortBy: "mem" active: root._showPanel onProcessesChanged: hoverPanel.keepOpen(300) } - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: "\uEFC5" anchors.verticalCenter: parent.verticalCenter diff --git a/shell/modules/MprisModule.qml b/shell/modules/MprisModule.qml index 0edbf0e..cd32262 100644 --- a/shell/modules/MprisModule.qml +++ b/shell/modules/MprisModule.qml @@ -6,12 +6,12 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing opacity: S.Modules.mpris.enable && player !== null ? 1 : 0 visible: opacity > 0 - tooltip: "" + _panelHovered: hoverPanel.panelHovered readonly property var _players: S.MprisService.players readonly property MprisPlayer player: S.MprisService.player @@ -71,23 +71,6 @@ M.BarSection { required property var bar - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: root.playing ? "\uF04B" : (root.player?.playbackState === MprisPlaybackState.Paused ? "\uDB80\uDFE4" : "\uDB81\uDCDB") anchors.verticalCenter: parent.verticalCenter diff --git a/shell/modules/NetworkModule.qml b/shell/modules/NetworkModule.qml index 5624489..df827c2 100644 --- a/shell/modules/NetworkModule.qml +++ b/shell/modules/NetworkModule.qml @@ -4,30 +4,13 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing - tooltip: "" + _panelHovered: hoverPanel.panelHovered readonly property string state: S.NetworkService.state - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: { if (root.state === "wifi") diff --git a/shell/modules/NotificationsModule.qml b/shell/modules/NotificationsModule.qml index 7df4b2b..1af6743 100644 --- a/shell/modules/NotificationsModule.qml +++ b/shell/modules/NotificationsModule.qml @@ -5,30 +5,13 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing - tooltip: "" + _panelHovered: hoverPanel.panelHovered readonly property bool hasUrgent: S.NotifService.list.some(n => n.urgency === NotificationUrgency.Critical && n.state !== "dismissed") - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: { if (S.NotifService.dnd) diff --git a/shell/modules/PinnableSection.qml b/shell/modules/PinnableSection.qml new file mode 100644 index 0000000..5a4c1e3 --- /dev/null +++ b/shell/modules/PinnableSection.qml @@ -0,0 +1,30 @@ +import QtQuick +import Quickshell +import "." as M +import "../services" as S + +// Base component for bar modules with a pinnable hover panel. +// Provides the _pinned/_anyHover/_showPanel/_unpinTimer boilerplate. +// Modules bind _panelHovered to their HoverPanel's panelHovered property. +M.BarSection { + id: root + tooltip: "" + + property bool _pinned: false + property bool _panelHovered: false + readonly property bool _anyHover: root._hovered || _panelHovered + readonly property bool _showPanel: _anyHover || _pinned + + on_AnyHoverChanged: { + if (_anyHover) + _unpinTimer.stop(); + else if (_pinned) + _unpinTimer.start(); + } + + Timer { + id: _unpinTimer + interval: 500 + onTriggered: root._pinned = false + } +} diff --git a/shell/modules/TemperatureModule.qml b/shell/modules/TemperatureModule.qml index 89bd8e5..b352759 100644 --- a/shell/modules/TemperatureModule.qml +++ b/shell/modules/TemperatureModule.qml @@ -4,10 +4,10 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: Math.max(1, S.Theme.moduleSpacing - 2) - tooltip: "" + _panelHovered: hoverPanel.panelHovered readonly property int _warm: S.Modules.temperature.warm || 80 readonly property int _hot: S.Modules.temperature.hot || 90 @@ -30,23 +30,6 @@ M.BarSection { } } - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } - M.BarIcon { icon: "\uF2C9" color: root._stateColor diff --git a/shell/modules/WeatherModule.qml b/shell/modules/WeatherModule.qml index b9f7f02..b22b24c 100644 --- a/shell/modules/WeatherModule.qml +++ b/shell/modules/WeatherModule.qml @@ -4,28 +4,11 @@ import "." as M import "../services" as S import "../applets" as C -M.BarSection { +M.PinnableSection { id: root spacing: S.Theme.moduleSpacing - tooltip: "" visible: S.Modules.weather.enable && S.WeatherService.available - - property bool _pinned: false - readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered - readonly property bool _showPanel: _anyHover || _pinned - - on_AnyHoverChanged: { - if (_anyHover) - _unpinTimer.stop(); - else if (_pinned) - _unpinTimer.start(); - } - - Timer { - id: _unpinTimer - interval: 500 - onTriggered: root._pinned = false - } + _panelHovered: hoverPanel.panelHovered M.BarIcon { icon: S.WeatherService.icon diff --git a/shell/modules/qmldir b/shell/modules/qmldir index 3a8eee3..8d09375 100644 --- a/shell/modules/qmldir +++ b/shell/modules/qmldir @@ -23,6 +23,7 @@ NotifCard 1.0 NotifCard.qml NotifPopup 1.0 NotifPopup.qml NotificationsModule 1.0 NotificationsModule.qml OverviewBackdrop 1.0 OverviewBackdrop.qml +PinnableSection 1.0 PinnableSection.qml PopupBackground 1.0 PopupBackground.qml PowerMenu 1.0 PowerMenu.qml PowerModule 1.0 PowerModule.qml