diff --git a/settings/hyprland.nix b/settings/hyprland.nix index b1a7100..2853802 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -1330,65 +1330,73 @@ in id: menuOpener } - Column { - id: menuItems + Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 6 - width: 200 + width: menuItems.width + 16 + height: menuItems.height + 12 + radius: 10 + color: Theme.base01 - Repeater { - model: menuOpener.children + Column { + id: menuItems + anchors.centerIn: parent + width: 200 - Rectangle { - required property var modelData - width: 200 - height: modelData.isSeparator ? 9 : 28 - color: !modelData.isSeparator && itemMouse.containsMouse && modelData.enabled - ? Theme.base02 : "transparent" - radius: modelData.isSeparator ? 0 : 4 + Repeater { + model: menuOpener.children Rectangle { - visible: modelData.isSeparator - anchors.centerIn: parent - width: parent.width - 20 - height: 1 - color: Theme.base03 - } + required property var modelData + width: 200 + height: modelData.isSeparator ? 9 : 28 + color: !modelData.isSeparator && itemMouse.containsMouse && modelData.enabled + ? Theme.base02 : "transparent" + radius: modelData.isSeparator ? 0 : 4 - RowLayout { - visible: !modelData.isSeparator - anchors.fill: parent - anchors.leftMargin: 10 - anchors.rightMargin: 10 - spacing: 8 - - Text { - Layout.fillWidth: true - text: modelData.text ?? "" - color: modelData.enabled ? Theme.base05 : Theme.base03 - font.family: "FiraMono Nerd Font" - font.pixelSize: 12 - elide: Text.ElideRight + Rectangle { + visible: modelData.isSeparator + anchors.centerIn: parent + width: parent.width - 20 + height: 1 + color: Theme.base03 } - Text { - visible: modelData.buttonType !== QsMenuButtonType.None - text: modelData.checkState === Qt.Checked ? "\u2713" : "" - color: Theme.base0D - font.family: "FiraMono Nerd Font" - font.pixelSize: 12 - } - } + RowLayout { + visible: !modelData.isSeparator + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + spacing: 8 - MouseArea { - id: itemMouse - anchors.fill: parent - hoverEnabled: true - enabled: !modelData.isSeparator && modelData.enabled - onClicked: { - modelData.triggered(); - bar.closeAllDropdowns(); + Text { + Layout.fillWidth: true + text: modelData.text ?? "" + color: modelData.enabled ? Theme.base05 : Theme.base03 + font.family: "FiraMono Nerd Font" + font.pixelSize: 12 + elide: Text.ElideRight + } + + Text { + visible: modelData.buttonType !== QsMenuButtonType.None + text: modelData.checkState === Qt.Checked ? "\u2713" : "" + color: Theme.base0D + font.family: "FiraMono Nerd Font" + font.pixelSize: 12 + } + } + + MouseArea { + id: itemMouse + anchors.fill: parent + hoverEnabled: true + enabled: !modelData.isSeparator && modelData.enabled + onClicked: { + modelData.triggered(); + bar.closeAllDropdowns(); + } } } } @@ -1404,184 +1412,192 @@ in fullHeight: volDropdownCol.height + 16 autoCloseMs: 3000 - Column { - id: volDropdownCol + Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 8 - width: 260 - spacing: 8 + width: volDropdownCol.width + 20 + height: volDropdownCol.height + 16 + radius: 10 + color: Theme.base01 - // Master volume - Text { - text: "\u{f057e} Master" - color: Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 13 - font.weight: Font.Medium - } - - Row { - width: parent.width + Column { + id: volDropdownCol + anchors.centerIn: parent + width: 260 spacing: 8 - Rectangle { - id: masterSliderBg - width: parent.width - masterVolLabel.width - 8 - height: 20 - radius: 4 - color: Theme.base01 - anchors.verticalCenter: parent.verticalCenter + // Master volume + Text { + text: "\u{f057e} Master" + color: Theme.base05 + font.family: "FiraMono Nerd Font" + font.pixelSize: 13 + font.weight: Font.Medium + } + + Row { + width: parent.width + spacing: 8 Rectangle { - width: volWidget.sink && volWidget.sink.audio - ? Math.min(1, volWidget.sink.audio.volume) * parent.width : 0 - height: parent.height + id: masterSliderBg + width: parent.width - masterVolLabel.width - 8 + height: 20 radius: 4 - color: volWidget.muted ? Theme.base03 : Theme.base0D - Behavior on width { NumberAnimation { duration: 80 } } + color: Theme.base02 + anchors.verticalCenter: parent.verticalCenter + + Rectangle { + width: volWidget.sink && volWidget.sink.audio + ? Math.min(1, volWidget.sink.audio.volume) * parent.width : 0 + height: parent.height + radius: 4 + color: volWidget.muted ? Theme.base03 : Theme.base0D + Behavior on width { NumberAnimation { duration: 80 } } + } + + MouseArea { + anchors.fill: parent + onPressed: (mouse) => setVolume(mouse) + onPositionChanged: (mouse) => { if (pressed) setVolume(mouse); } + function setVolume(mouse) { + if (!volWidget.sink || !volWidget.sink.audio) return; + let v = Math.max(0, Math.min(1, mouse.x / width)); + volWidget.sink.audio.volume = v; + } + } } + Text { + id: masterVolLabel + width: 36 + text: volWidget.vol + "%" + color: Theme.base05 + font.family: "FiraMono Nerd Font" + font.pixelSize: 11 + horizontalAlignment: Text.AlignRight + anchors.verticalCenter: parent.verticalCenter + } + } + + // Mute button + Rectangle { + width: parent.width + height: 28 + color: masterMuteMa.containsMouse ? Theme.base02 : "transparent" + radius: 4 + + Text { + anchors.centerIn: parent + text: volWidget.muted ? "\u{f0581} Unmute" : "\u{f057e} Mute" + color: Theme.base05 + font.family: "FiraMono Nerd Font" + font.pixelSize: 12 + } MouseArea { + id: masterMuteMa anchors.fill: parent - onPressed: (mouse) => setVolume(mouse) - onPositionChanged: (mouse) => { if (pressed) setVolume(mouse); } - function setVolume(mouse) { - if (!volWidget.sink || !volWidget.sink.audio) return; - let v = Math.max(0, Math.min(1, mouse.x / width)); - volWidget.sink.audio.volume = v; + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (volWidget.sink && volWidget.sink.audio) + volWidget.sink.audio.muted = !volWidget.sink.audio.muted; } } } + // Separator + Rectangle { + width: parent.width - 20 + anchors.horizontalCenter: parent.horizontalCenter + height: 1 + color: Theme.base02 + visible: appStreamsCol.childrenRect.height > 0 + } + + // App streams header Text { - id: masterVolLabel - width: 36 - text: volWidget.vol + "%" + visible: appStreamsCol.childrenRect.height > 0 + text: "\u{f0641} Applications" color: Theme.base05 font.family: "FiraMono Nerd Font" - font.pixelSize: 11 - horizontalAlignment: Text.AlignRight - anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 13 + font.weight: Font.Medium } - } - // Mute button - Rectangle { - width: parent.width - height: 28 - color: masterMuteMa.containsMouse ? Theme.base02 : "transparent" - radius: 4 + // Per-app streams + Column { + id: appStreamsCol + width: parent.width + spacing: 6 - Text { - anchors.centerIn: parent - text: volWidget.muted ? "\u{f0581} Unmute" : "\u{f057e} Mute" - color: Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 12 - } - MouseArea { - id: masterMuteMa - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if (volWidget.sink && volWidget.sink.audio) - volWidget.sink.audio.muted = !volWidget.sink.audio.muted; - } - } - } + Repeater { + id: appStreamsRepeater + model: Pipewire.nodes - // Separator - Rectangle { - width: parent.width - 20 - anchors.horizontalCenter: parent.horizontalCenter - height: 1 - color: Theme.base02 - visible: appStreamsCol.childrenRect.height > 0 - } - - // App streams header - Text { - visible: appStreamsCol.childrenRect.height > 0 - text: "\u{f0641} Applications" - color: Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 13 - font.weight: Font.Medium - } - - // Per-app streams - Column { - id: appStreamsCol - width: parent.width - spacing: 6 - - Repeater { - id: appStreamsRepeater - model: Pipewire.nodes - - Column { - required property var modelData - width: parent.width - spacing: 2 - visible: modelData.isStream && modelData.audio !== null - - PwObjectTracker { - objects: [modelData] - } - - Text { - text: modelData.properties["application.name"] || modelData.name || "Unknown" - color: Theme.base04 - font.family: "FiraMono Nerd Font" - font.pixelSize: 11 - elide: Text.ElideRight + Column { + required property var modelData width: parent.width - } + spacing: 2 + visible: modelData.isStream && modelData.audio !== null - Row { - width: parent.width - spacing: 8 - - Rectangle { - width: parent.width - appVolLabel.width - 8 - height: 16 - radius: 3 - color: Theme.base01 - anchors.verticalCenter: parent.verticalCenter - - Rectangle { - width: modelData.audio - ? Math.min(1, modelData.audio.volume) * parent.width : 0 - height: parent.height - radius: 3 - color: modelData.audio && modelData.audio.muted - ? Theme.base03 : Theme.base0C - Behavior on width { NumberAnimation { duration: 80 } } - } - - MouseArea { - anchors.fill: parent - onPressed: (mouse) => setVol(mouse) - onPositionChanged: (mouse) => { if (pressed) setVol(mouse); } - function setVol(mouse) { - if (!modelData.audio) return; - let v = Math.max(0, Math.min(1, mouse.x / width)); - modelData.audio.volume = v; - } - } + PwObjectTracker { + objects: [modelData] } Text { - id: appVolLabel - width: 36 - text: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : "0%" + text: modelData.properties["application.name"] || modelData.name || "Unknown" color: Theme.base04 font.family: "FiraMono Nerd Font" - font.pixelSize: 10 - horizontalAlignment: Text.AlignRight - anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 11 + elide: Text.ElideRight + width: parent.width + } + + Row { + width: parent.width + spacing: 8 + + Rectangle { + width: parent.width - appVolLabel.width - 8 + height: 16 + radius: 3 + color: Theme.base02 + anchors.verticalCenter: parent.verticalCenter + + Rectangle { + width: modelData.audio + ? Math.min(1, modelData.audio.volume) * parent.width : 0 + height: parent.height + radius: 3 + color: modelData.audio && modelData.audio.muted + ? Theme.base03 : Theme.base0C + Behavior on width { NumberAnimation { duration: 80 } } + } + + MouseArea { + anchors.fill: parent + onPressed: (mouse) => setVol(mouse) + onPositionChanged: (mouse) => { if (pressed) setVol(mouse); } + function setVol(mouse) { + if (!modelData.audio) return; + let v = Math.max(0, Math.min(1, mouse.x / width)); + modelData.audio.volume = v; + } + } + } + + Text { + id: appVolLabel + width: 36 + text: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : "0%" + color: Theme.base04 + font.family: "FiraMono Nerd Font" + font.pixelSize: 10 + horizontalAlignment: Text.AlignRight + anchors.verticalCenter: parent.verticalCenter + } } } } @@ -1597,135 +1613,143 @@ in fullWidth: netDropdownCol.width + 24 fullHeight: netDropdownCol.height + 16 - Column { - id: netDropdownCol + Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 8 - width: 220 - spacing: 4 + width: netDropdownCol.width + 20 + height: netDropdownCol.height + 16 + radius: 10 + color: Theme.base01 - Text { - width: parent.width - text: netWidget.netState === "connected" - ? "\u{f05a9} " + netWidget.netConn - : "\u{f05aa} Not connected" - color: Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 13 - font.weight: Font.Medium - elide: Text.ElideRight - } - - Rectangle { - visible: netWidget.netState === "connected" - width: parent.width - height: 28 - color: disconnectMouse.containsMouse ? Theme.base02 : "transparent" - radius: 4 + Column { + id: netDropdownCol + anchors.centerIn: parent + width: 220 + spacing: 4 Text { - anchors.centerIn: parent - text: "Disconnect" - color: Theme.base08 + width: parent.width + text: netWidget.netState === "connected" + ? "\u{f05a9} " + netWidget.netConn + : "\u{f05aa} Not connected" + color: Theme.base05 font.family: "FiraMono Nerd Font" - font.pixelSize: 12 + font.pixelSize: 13 + font.weight: Font.Medium + elide: Text.ElideRight } - MouseArea { - id: disconnectMouse - anchors.fill: parent - hoverEnabled: true - onClicked: { - netDisconnectProc.targetDevice = netWidget.netDevice; - netDisconnectProc.running = true; - netWidget.netState = "disconnected"; - netWidget.netConn = ""; - netWidget.netIcon = "\u{f05aa}"; - bar.closeAllDropdowns(); - netRefreshDelay.start(); - } - } - } - - Rectangle { - width: parent.width - 20 - anchors.horizontalCenter: parent.horizontalCenter - height: 1 - color: Theme.base03 - } - - Text { - text: "Available networks" - color: Theme.base03 - font.family: "FiraMono Nerd Font" - font.pixelSize: 11 - topPadding: 2 - } - - Repeater { - model: netWidget.wifiNetworks - Rectangle { - required property var modelData - width: 220 - height: 32 - color: netItemMouse.containsMouse ? Theme.base02 : "transparent" + visible: netWidget.netState === "connected" + width: parent.width + height: 28 + color: disconnectMouse.containsMouse ? Theme.base02 : "transparent" radius: 4 - Row { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 8 - anchors.right: parent.right - anchors.rightMargin: 8 - spacing: 8 - - Text { - text: { - let s = modelData.signal; - if (s >= 75) return "\u{f05a9}"; - if (s >= 50) return "\u{f05a9}"; - if (s >= 25) return "\u{f05a9}"; - return "\u{f05aa}"; - } - color: modelData.active ? Theme.base0B : Theme.base04 - font.family: "FiraMono Nerd Font" - font.pixelSize: 13 - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: modelData.ssid - color: modelData.active ? Theme.base0B : Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 12 - elide: Text.ElideRight - width: 140 - anchors.verticalCenter: parent.verticalCenter - } - - Text { - visible: modelData.security !== "" && modelData.security !== "--" - text: "\u{f0341}" - color: Theme.base03 - font.family: "FiraMono Nerd Font" - font.pixelSize: 10 - anchors.verticalCenter: parent.verticalCenter - } + Text { + anchors.centerIn: parent + text: "Disconnect" + color: Theme.base08 + font.family: "FiraMono Nerd Font" + font.pixelSize: 12 } MouseArea { - id: netItemMouse + id: disconnectMouse anchors.fill: parent hoverEnabled: true onClicked: { - if (!modelData.active) { - wifiConnectProc.targetSsid = modelData.ssid; - wifiConnectProc.running = true; - netRefreshDelay.start(); - } + netDisconnectProc.targetDevice = netWidget.netDevice; + netDisconnectProc.running = true; + netWidget.netState = "disconnected"; + netWidget.netConn = ""; + netWidget.netIcon = "\u{f05aa}"; bar.closeAllDropdowns(); + netRefreshDelay.start(); + } + } + } + + Rectangle { + width: parent.width - 20 + anchors.horizontalCenter: parent.horizontalCenter + height: 1 + color: Theme.base03 + } + + Text { + text: "Available networks" + color: Theme.base03 + font.family: "FiraMono Nerd Font" + font.pixelSize: 11 + topPadding: 2 + } + + Repeater { + model: netWidget.wifiNetworks + + Rectangle { + required property var modelData + width: 220 + height: 32 + color: netItemMouse.containsMouse ? Theme.base02 : "transparent" + radius: 4 + + Row { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + anchors.right: parent.right + anchors.rightMargin: 8 + spacing: 8 + + Text { + text: { + let s = modelData.signal; + if (s >= 75) return "\u{f05a9}"; + if (s >= 50) return "\u{f05a9}"; + if (s >= 25) return "\u{f05a9}"; + return "\u{f05aa}"; + } + color: modelData.active ? Theme.base0B : Theme.base04 + font.family: "FiraMono Nerd Font" + font.pixelSize: 13 + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: modelData.ssid + color: modelData.active ? Theme.base0B : Theme.base05 + font.family: "FiraMono Nerd Font" + font.pixelSize: 12 + elide: Text.ElideRight + width: 140 + anchors.verticalCenter: parent.verticalCenter + } + + Text { + visible: modelData.security !== "" && modelData.security !== "--" + text: "\u{f0341}" + color: Theme.base03 + font.family: "FiraMono Nerd Font" + font.pixelSize: 10 + anchors.verticalCenter: parent.verticalCenter + } + } + + MouseArea { + id: netItemMouse + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (!modelData.active) { + wifiConnectProc.targetSsid = modelData.ssid; + wifiConnectProc.running = true; + netRefreshDelay.start(); + } + bar.closeAllDropdowns(); + } } } } @@ -1741,109 +1765,117 @@ in fullWidth: batteryDropdownCol.width + 24 fullHeight: batteryDropdownCol.height + 16 - Column { - id: batteryDropdownCol + Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top - anchors.topMargin: 10 - width: 200 - spacing: 8 + anchors.topMargin: 8 + width: batteryDropdownCol.width + 20 + height: batteryDropdownCol.height + 16 + radius: 10 + color: Theme.base01 - Row { - width: parent.width + Column { + id: batteryDropdownCol + anchors.centerIn: parent + width: 200 spacing: 8 - Text { - text: batteryWidget.batteryIcon - color: Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 18 - anchors.verticalCenter: parent.verticalCenter - } + Row { + width: parent.width + spacing: 8 - Column { - anchors.verticalCenter: parent.verticalCenter Text { - text: batteryWidget.batteryLevel + "%" + (batteryWidget.charging ? " — Charging" : "") + text: batteryWidget.batteryIcon color: Theme.base05 font.family: "FiraMono Nerd Font" - font.pixelSize: 13 - font.weight: Font.Medium + font.pixelSize: 18 + anchors.verticalCenter: parent.verticalCenter } - Text { - text: batteryWidget.powerDraw.toFixed(1) + " W" - + (batteryWidget.timeRemaining !== "" ? " \u2022 " + batteryWidget.timeRemaining + (batteryWidget.charging ? " to full" : " left") : "") - color: Theme.base04 - font.family: "FiraMono Nerd Font" - font.pixelSize: 11 + + Column { + anchors.verticalCenter: parent.verticalCenter + Text { + text: batteryWidget.batteryLevel + "%" + (batteryWidget.charging ? " — Charging" : "") + color: Theme.base05 + font.family: "FiraMono Nerd Font" + font.pixelSize: 13 + font.weight: Font.Medium + } + Text { + text: batteryWidget.powerDraw.toFixed(1) + " W" + + (batteryWidget.timeRemaining !== "" ? " \u2022 " + batteryWidget.timeRemaining + (batteryWidget.charging ? " to full" : " left") : "") + color: Theme.base04 + font.family: "FiraMono Nerd Font" + font.pixelSize: 11 + } } } - } - Rectangle { - width: parent.width - 10 - anchors.horizontalCenter: parent.horizontalCenter - height: 1 - color: Theme.base03 - } + Rectangle { + width: parent.width - 10 + anchors.horizontalCenter: parent.horizontalCenter + height: 1 + color: Theme.base03 + } - Text { - text: "Power Profile" - color: Theme.base03 - font.family: "FiraMono Nerd Font" - font.pixelSize: 11 - } + Text { + text: "Power Profile" + color: Theme.base03 + font.family: "FiraMono Nerd Font" + font.pixelSize: 11 + } - Row { - width: parent.width - spacing: 4 + Row { + width: parent.width + spacing: 4 - Repeater { - model: [ - { name: "power-saver", label: "\u{f0425}", tip: "Saver" }, - { name: "balanced", label: "\u{f0376}", tip: "Balanced" }, - { name: "performance", label: "\u{f0e0e}", tip: "Performance" } - ] + Repeater { + model: [ + { name: "power-saver", label: "\u{f0425}", tip: "Saver" }, + { name: "balanced", label: "\u{f0376}", tip: "Balanced" }, + { name: "performance", label: "\u{f0e0e}", tip: "Performance" } + ] - Rectangle { - required property var modelData - width: (parent.width - 8) / 3 - height: 36 - radius: 6 - color: batteryWidget.powerProfile === modelData.name - ? Theme.base02 : profMouse.containsMouse - ? Theme.base01 : "transparent" - border.width: batteryWidget.powerProfile === modelData.name ? 1 : 0 - border.color: Theme.base03 + Rectangle { + required property var modelData + width: (parent.width - 8) / 3 + height: 36 + radius: 6 + color: batteryWidget.powerProfile === modelData.name + ? Theme.base02 : profMouse.containsMouse + ? Theme.base01 : "transparent" + border.width: batteryWidget.powerProfile === modelData.name ? 1 : 0 + border.color: Theme.base03 - Column { - anchors.centerIn: parent - spacing: 1 - Text { - anchors.horizontalCenter: parent.horizontalCenter - text: modelData.label - color: batteryWidget.powerProfile === modelData.name - ? Theme.base0D : Theme.base05 - font.family: "FiraMono Nerd Font" - font.pixelSize: 14 + Column { + anchors.centerIn: parent + spacing: 1 + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: modelData.label + color: batteryWidget.powerProfile === modelData.name + ? Theme.base0D : Theme.base05 + font.family: "FiraMono Nerd Font" + font.pixelSize: 14 + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: modelData.tip + color: Theme.base04 + font.family: "FiraMono Nerd Font" + font.pixelSize: 9 + } } - Text { - anchors.horizontalCenter: parent.horizontalCenter - text: modelData.tip - color: Theme.base04 - font.family: "FiraMono Nerd Font" - font.pixelSize: 9 - } - } - MouseArea { - id: profMouse - anchors.fill: parent - hoverEnabled: true - onClicked: { - setProfileProc.target = modelData.name; - setProfileProc.running = true; - batteryWidget.powerProfile = modelData.name; + MouseArea { + id: profMouse + anchors.fill: parent + hoverEnabled: true + onClicked: { + setProfileProc.target = modelData.name; + setProfileProc.running = true; + batteryWidget.powerProfile = modelData.name; + } } } }