diff --git a/modules/Cpu.qml b/modules/Cpu.qml index 58d41e7..8159533 100644 --- a/modules/Cpu.qml +++ b/modules/Cpu.qml @@ -47,6 +47,7 @@ M.BarSection { property M.ProcessList _procs: M.ProcessList { sortBy: "cpu" active: root._showPanel + onProcessesChanged: hoverPanel.keepOpen(300) } on_AnyHoverChanged: { diff --git a/modules/HoverPanel.qml b/modules/HoverPanel.qml index d511214..47f6dc3 100644 --- a/modules/HoverPanel.qml +++ b/modules/HoverPanel.qml @@ -31,6 +31,7 @@ PanelWindow { // Shared required property color accentColor + property string panelTitle: "" property string panelNamespace: "nova-panel" property real contentWidth: 220 @@ -49,7 +50,7 @@ PanelWindow { x: panelContainer.x + panelContainer.width - 28 y: 0 width: 28 - height: 28 + height: 24 } WlrLayershell.layer: WlrLayer.Overlay @@ -91,6 +92,24 @@ PanelWindow { } } + // Content-change grace: call keepOpen(ms) when panel content is about to + // resize/rebuild (session switch, device list change, etc.) to prevent the + // hover-drop-on-resize from closing the panel. + property bool _contentBusy: false + Timer { + id: _contentBusyTimer + onTriggered: { + root._contentBusy = false; + if (!root.showPanel && !root._grace && !root._pinned) + root._hideTimer.restart(); + } + } + function keepOpen(ms) { + _contentBusy = true; + _contentBusyTimer.interval = ms ?? 400; + _contentBusyTimer.restart(); + } + function _show() { _updatePosition(); _winVisible = true; @@ -114,7 +133,7 @@ PanelWindow { Timer { id: _hideTimer interval: 150 - onTriggered: if (!root.showPanel && !root._grace && !root._pinned) + onTriggered: if (!root.showPanel && !root._grace && !root._pinned && !root._contentBusy) root.dismiss() } @@ -192,7 +211,7 @@ PanelWindow { x: 0 y: -height width: root.contentWidth - height: panelContent.height + height: _panelColumn.height opacity: 0 // Popup mode: eat clicks on panel background so outer dismiss doesn't fire @@ -209,52 +228,75 @@ PanelWindow { } Column { - id: panelContent + id: _panelColumn width: root.contentWidth - } - // Pin button — top-right corner, hover mode only - Item { - visible: !root.popupMode && (root.panelHovered || root._pinned) - x: parent.width - width - 4 - y: 4 - width: 20 - height: 20 - z: 2 - opacity: pinHover.hovered || root._pinned ? 1 : 0.35 + // Header row: title (optional) + pin button — hover mode only + Item { + visible: !root.popupMode + width: parent.width + height: 24 - Behavior on opacity { - NumberAnimation { - duration: 100 + Text { + visible: root.panelTitle !== "" + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: root.panelTitle + color: root.accentColor + font.pixelSize: M.Theme.fontSize - 1 + font.bold: true + font.family: M.Theme.fontFamily } - } - HoverHandler { - id: pinHover - } + Item { + visible: root.panelHovered || root._pinned + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + width: 20 + height: 20 + opacity: pinHover.hovered || root._pinned ? 1 : 0.35 - TapHandler { - cursorShape: Qt.PointingHandCursor - onTapped: { - root._pinned = !root._pinned; - if (!root._pinned && !root.showPanel) - root.dismiss(); - } - } + Behavior on opacity { + NumberAnimation { + duration: 100 + } + } - Text { - anchors.centerIn: parent - text: root._pinned ? "\uDB81\uDC03" : "\uDB82\uDD31" - color: root._pinned ? root.accentColor : M.Theme.base04 - font.pixelSize: M.Theme.fontSize - 1 - font.family: M.Theme.iconFontFamily + HoverHandler { + id: pinHover + } - Behavior on color { - ColorAnimation { - duration: 100 + TapHandler { + cursorShape: Qt.PointingHandCursor + onTapped: { + root._pinned = !root._pinned; + if (!root._pinned && !root.showPanel) + root.dismiss(); + } + } + + Text { + anchors.centerIn: parent + text: root._pinned ? "\uDB81\uDC03" : "\uDB82\uDD31" + color: root._pinned ? root.accentColor : M.Theme.base04 + font.pixelSize: M.Theme.fontSize - 1 + font.family: M.Theme.iconFontFamily + + Behavior on color { + ColorAnimation { + duration: 100 + } + } } } } + + Column { + id: panelContent + width: parent.width + } } } diff --git a/modules/Memory.qml b/modules/Memory.qml index 5d8b7a0..c9df8bc 100644 --- a/modules/Memory.qml +++ b/modules/Memory.qml @@ -24,6 +24,7 @@ M.BarSection { property M.ProcessList _procs: M.ProcessList { sortBy: "mem" active: root._showPanel + onProcessesChanged: hoverPanel.keepOpen(300) } on_AnyHoverChanged: { diff --git a/modules/Mpris.qml b/modules/Mpris.qml index d24c39e..4fd58ef 100644 --- a/modules/Mpris.qml +++ b/modules/Mpris.qml @@ -355,45 +355,63 @@ M.BarSection { } // Player switcher - Row { - anchors.horizontalCenter: parent.horizontalCenter - height: 22 - spacing: 6 + Item { + width: parent.width + height: _players.length > 1 ? 28 : 0 + visible: _players.length > 1 - Repeater { - model: root._players + Flickable { + id: _switcher + anchors.centerIn: parent + width: Math.min(_playerRow.implicitWidth, parent.width - 16) + height: 22 + contentWidth: _playerRow.implicitWidth + clip: true - delegate: Rectangle { - required property var modelData - required property int index + Row { + id: _playerRow + height: 22 + spacing: 6 - readonly property bool _active: index === root._playerIdx + Repeater { + model: root._players - width: _pLabel.implicitWidth + 12 - height: 18 - radius: 9 - color: _active ? M.Theme.base02 : (pHover.hovered ? M.Theme.base02 : "transparent") - border.color: _active ? root.accentColor : M.Theme.base03 - border.width: _active ? 1 : 0 - anchors.verticalCenter: parent.verticalCenter + delegate: Rectangle { + required property var modelData + required property int index - Text { - id: _pLabel - anchors.centerIn: parent - text: modelData.identity ?? "Player" - color: _active ? root.accentColor : M.Theme.base04 - font.pixelSize: M.Theme.fontSize - 2 - font.family: M.Theme.fontFamily - font.bold: _active - } + readonly property bool _active: index === root._playerIdx - HoverHandler { - id: pHover - cursorShape: Qt.PointingHandCursor - } + width: _pLabel.implicitWidth + 12 + height: 18 + radius: 9 + color: _active ? M.Theme.base02 : (pHover.hovered ? M.Theme.base02 : "transparent") + border.color: _active ? root.accentColor : M.Theme.base03 + border.width: _active ? 1 : 0 + anchors.verticalCenter: parent.verticalCenter - TapHandler { - onTapped: root._playerIdx = index + Text { + id: _pLabel + anchors.centerIn: parent + text: modelData.identity ?? "Player" + color: _active ? root.accentColor : M.Theme.base04 + font.pixelSize: M.Theme.fontSize - 2 + font.family: M.Theme.fontFamily + font.bold: _active + } + + HoverHandler { + id: pHover + cursorShape: Qt.PointingHandCursor + } + + TapHandler { + onTapped: { + root._playerIdx = index; + hoverPanel.keepOpen(400); + } + } + } } } } diff --git a/modules/Network.qml b/modules/Network.qml index f0d3865..033c0ab 100644 --- a/modules/Network.qml +++ b/modules/Network.qml @@ -98,12 +98,12 @@ M.BarSection { required property var bar - readonly property bool _anyHover: root._hovered || networkMenu.panelHovered || networkMenu._busy + readonly property bool _anyHover: root._hovered || networkMenu.panelHovered M.NetworkMenu { id: networkMenu showPanel: root._anyHover - screen: root.bar.screen + screen: QsWindow.window?.screen ?? null anchorItem: root accentColor: root.accentColor } diff --git a/modules/NetworkMenu.qml b/modules/NetworkMenu.qml index ad493a2..529458e 100644 --- a/modules/NetworkMenu.qml +++ b/modules/NetworkMenu.qml @@ -12,8 +12,6 @@ M.HoverPanel { onVisibleChanged: if (visible) scanner.running = true - readonly property bool _busy: connectProc.running || disconnectProc.running || radioProc.running - function triggerRefresh() { if (visible) scanner.running = true; @@ -85,24 +83,30 @@ M.HoverPanel { id: radioProc property string _state: "" command: ["nmcli", "radio", "wifi", _state] - onRunningChanged: if (!running) - scanner.running = true + onRunningChanged: if (!running) { + scanner.running = true; + menuWindow.keepOpen(500); + } } property Process _connectProc: Process { id: connectProc property string uuid: "" command: ["nmcli", "connection", "up", uuid] - onRunningChanged: if (!running) - scanner.running = true + onRunningChanged: if (!running) { + scanner.running = true; + menuWindow.keepOpen(500); + } } property Process _disconnectProc: Process { id: disconnectProc property string uuid: "" command: ["nmcli", "connection", "down", uuid] - onRunningChanged: if (!running) - scanner.running = true + onRunningChanged: if (!running) { + scanner.running = true; + menuWindow.keepOpen(500); + } } // Wi-Fi radio toggle header diff --git a/modules/Volume.qml b/modules/Volume.qml index 3283ed7..7166207 100644 --- a/modules/Volume.qml +++ b/modules/Volume.qml @@ -213,7 +213,7 @@ M.BarSection { verticalAlignment: Text.AlignVCenter leftPadding: 12 text: "Output Devices" - color: M.Theme.base04 + color: root.accentColor font.pixelSize: M.Theme.fontSize - 1 font.family: M.Theme.fontFamily } @@ -279,7 +279,7 @@ M.BarSection { verticalAlignment: Text.AlignVCenter leftPadding: 12 text: "Applications" - color: M.Theme.base04 + color: root.accentColor font.pixelSize: M.Theme.fontSize - 1 font.family: M.Theme.fontFamily }