diff --git a/settings/quickshell.nix b/settings/quickshell.nix index d58e173..5a3fb5a 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -545,7 +545,12 @@ in property string base03: "${c.base03}" property string base04: "${c.base04}" property string base05: "${c.base05}" + property string base09: "${c.base09}" + property string base0A: "${c.base0A}" + property string base0B: "${c.base0B}" + property string base0C: "${c.base0C}" property string base0D: "${c.base0D}" + property string base0E: "${c.base0E}" } } @@ -563,23 +568,52 @@ in readonly property color base03: "#" + pal.base03 readonly property color base04: "#" + pal.base04 readonly property color base05: "#" + pal.base05 - // The one themed accent: active, focused, selected, filled. - readonly property color base0D: "#" + pal.base0D + // The one themed accent: active, focused, selected, filled — + // and every status colour below is harmonised toward it, so a + // bad pick tints the whole bar rather than just the highlights. + // + // NOT pal.base0D on its own. The generator fills each slot + // independently and is free to hand that one a colour from + // outside the rest of the palette's family: on the amber + // wallhaven-5y39p9 every accent slot comes back orange + // (H=0.08) except base0D, which comes back blue-grey (8590ae, + // H=0.62) — that was a blue NixOS logo in an otherwise amber + // bar. So audition all six and take whichever sits nearest a + // good accent saturation. + // + // Nearest 0.6, not "the most saturated": max picks the neon + // fe5162 (S=0.99) on wallhaven-k8dqr6, near enough to `err` + // that errors stop reading as errors. On a monochrome + // wallpaper every slot collapses to the same value, so the + // choice stops mattering rather than going wrong. + readonly property real accentSat: 0.60 + readonly property color accent: { + const slots = [pal.base09, pal.base0A, pal.base0B, + pal.base0C, pal.base0D, pal.base0E]; + let best = Qt.color("#" + pal.base0D); + let bestD = Math.abs(best.hslSaturation - accentSat); + for (let i = 0; i < slots.length; i++) { + const c = Qt.color("#" + slots[i]); + const d = Math.abs(c.hslSaturation - accentSat); + if (d < bestD) { best = c; bestD = d; } + } + return best; + } // ── Status colours: fixed hues, pulled toward the theme. // Sourcing these from stylix doesn't work — base08-base0F all // collapse to near-identical values on a monochrome wallpaper, // which leaves "battery critical" and "battery low" the same // colour. So the HUE is fixed and only the hue is: each one is - // then mixed toward base0D, which keeps red reading as red + // then mixed toward the accent, which keeps red reading as red // while landing it in the wallpaper's family instead of looking - // pasted on. Blending toward base0D and not toward the card + // pasted on. Blending toward the accent and not toward the card // keeps luminance roughly intact, so these stay legible as text // on cardBg — mixing into the background would sink them. readonly property real statusHarmony: 0.25 // 0 = pure hue, 1 = pure accent function harmonised(hex) { let h = Qt.color(hex); - return Qt.tint(base0D, Qt.rgba(h.r, h.g, h.b, 1 - statusHarmony)); + return Qt.tint(accent, Qt.rgba(h.r, h.g, h.b, 1 - statusHarmony)); } readonly property color err: harmonised("#e0525f") // critical: temp ≥85, batt ≤15, auth failed readonly property color warn: harmonised("#e0a44b") // caution: temp ≥70, batt ≤30 @@ -803,7 +837,7 @@ in radius: Theme.radiusCard color: Theme.cardBg border.width: Theme.borderWidth - border.color: pam.active ? Theme.base0D + border.color: pam.active ? Theme.accent : root.failed ? Theme.err : Theme.base03 Behavior on border.color { ColorAnimation { duration: Theme.animFade } } @@ -863,7 +897,7 @@ in text: "arrow-right" font.family: Theme.iconFont font.pixelSize: 18 - color: Theme.base0D + color: Theme.accent } MouseArea { id: confirmMa @@ -991,7 +1025,7 @@ in font.family: Theme.fontFamily font.pixelSize: 15 font.bold: true - color: Theme.base0D + color: Theme.accent bottomPadding: 4 } @@ -1006,7 +1040,7 @@ in text: modelData[0] font.family: Theme.fontFamily font.pixelSize: 13 - color: Theme.base0D + color: Theme.accent } Text { text: modelData[1] @@ -2010,7 +2044,7 @@ in // variants scale with just a trackH override. component PillSlider: Item { property real value: 0 - property color fillColor: Theme.base0D + property color fillColor: Theme.accent property real trackH: 8 readonly property real knobD: trackH + 8 signal moved(real v) @@ -2052,7 +2086,7 @@ in // something you can grab. component MeterBar: Item { property real value: 0 - property color fillColor: Theme.base0D + property color fillColor: Theme.accent property real trackH: 5 height: trackH Rectangle { @@ -2074,7 +2108,7 @@ in property bool on: false signal toggled(bool v) width: 34; height: 18; radius: 9 - color: on ? Theme.base0D : Theme.base02 + color: on ? Theme.accent : Theme.base02 Behavior on color { ColorAnimation { duration: Theme.animFade } } Rectangle { width: 12; height: 12; radius: 6 @@ -3264,14 +3298,14 @@ in ColorOverlay { anchors.fill: launchLogo source: launchLogo - color: Theme.base0D + color: Theme.accent } } SText { anchors.verticalCenter: parent.verticalCenter text: "${config.home.username}" - color: Theme.base0D + color: Theme.accent } SText { @@ -3300,7 +3334,7 @@ in anchors.verticalCenter: parent.verticalCenter text: "equal" font.pixelSize: 18 - color: Theme.base0D + color: Theme.accent } SText { anchors.verticalCenter: parent.verticalCenter @@ -3359,7 +3393,7 @@ in anchors.verticalCenter: parent.verticalCenter text: "sparkles" font.pixelSize: 14 - color: Theme.base0D + color: Theme.accent } SText { anchors.verticalCenter: parent.verticalCenter @@ -3682,7 +3716,7 @@ in visible: icon === "" text: glyph font.pixelSize: 20 - color: Theme.base0D + color: Theme.accent } } @@ -3779,7 +3813,7 @@ in anchors.verticalCenter: parent.verticalCenter text: modelData.icon font.pixelSize: 20 - color: Theme.base0D + color: Theme.accent } Column { anchors.verticalCenter: parent.verticalCenter @@ -3890,7 +3924,7 @@ in anchors.verticalCenter: parent.verticalCenter text: ctxMenu.pinned ? "pin-off" : "pin" font.pixelSize: 14 - color: Theme.base0D + color: Theme.accent } SText { anchors.verticalCenter: parent.verticalCenter @@ -4092,7 +4126,7 @@ in ColorOverlay { anchors.fill: nixLogoImg source: nixLogoImg - color: Theme.base0D + color: Theme.accent } MouseArea { @@ -4126,7 +4160,7 @@ in width: 6 height: wsItem.modelData.focused ? 18 : 6 radius: 3 - color: wsItem.modelData.focused ? Theme.base0D + color: wsItem.modelData.focused ? Theme.accent : wsMa.containsMouse ? Theme.base04 : Theme.base03 Behavior on height { NumberAnimation { duration: 200; easing.type: Easing.OutExpo } @@ -4165,7 +4199,7 @@ in anchors.centerIn: parent text: "image" font.pixelSize: 18 - color: bar.activeDropdown === wallDropdown ? Theme.base0D + color: bar.activeDropdown === wallDropdown ? Theme.accent : wallMa.containsMouse ? Theme.base05 : Theme.base04 Behavior on color { ColorAnimation { duration: Theme.animFade } } } @@ -4338,7 +4372,7 @@ in GlanceStat { value: srvWidget.connected ? Math.round(srvWidget.cpu) + "%" : "--" caption: "cpu" - valueColor: srvWidget.connected ? Theme.base0D : Theme.base03 + valueColor: srvWidget.connected ? Theme.accent : Theme.base03 } GlanceStat { value: srvWidget.connected && srvWidget.memTotal > 0 @@ -5268,7 +5302,7 @@ in anchors.verticalCenter: parent.verticalCenter visible: modelData.buttonType !== QsMenuButtonType.None text: modelData.checkState === Qt.Checked ? "\u2713" : "" - color: Theme.base0D + color: Theme.accent font.pixelSize: 12 } @@ -5440,7 +5474,7 @@ in // hover — same selection language as the // workspace pill above it. border.width: Theme.borderWidth - border.color: active ? Theme.base0D + border.color: active ? Theme.accent : wallTileMa.containsMouse ? Theme.base04 : "transparent" Behavior on border.color { ColorAnimation { duration: Theme.animFade } } @@ -5517,7 +5551,7 @@ in width: parent.width - 42 - 76 - 20 anchors.verticalCenter: parent.verticalCenter value: srvWidget.cpu / 100 - fillColor: srvWidget.cpu >= 90 ? Theme.err : Theme.base0D + fillColor: srvWidget.cpu >= 90 ? Theme.err : Theme.accent } SText { width: 76 @@ -5697,7 +5731,7 @@ in width: parent.width - masterVolLabel.width - 8 anchors.verticalCenter: parent.verticalCenter value: volWidget.sink && volWidget.sink.audio ? Math.min(1, volWidget.sink.audio.volume) : 0 - fillColor: volWidget.muted ? Theme.base03 : Theme.base0D + fillColor: volWidget.muted ? Theme.base03 : Theme.accent onMoved: (v) => { if (volWidget.sink && volWidget.sink.audio) volWidget.sink.audio.volume = v; } } @@ -5810,8 +5844,8 @@ in width: (parent.width - 12) / 3 height: 26 radius: 6 - color: active ? Theme.base0D : "transparent" - border.color: active ? Theme.base0D : Theme.base03 + color: active ? Theme.accent : "transparent" + border.color: active ? Theme.accent : Theme.base03 border.width: 1 SText { @@ -5892,7 +5926,7 @@ in height: 16 trackH: 4 value: modelData.audio ? Math.min(1, modelData.audio.volume) : 0 - fillColor: modelData.audio && modelData.audio.muted ? Theme.base03 : Theme.base0D + fillColor: modelData.audio && modelData.audio.muted ? Theme.base03 : Theme.accent onMoved: (v) => { if (modelData.audio) modelData.audio.volume = v; } } @@ -6355,7 +6389,7 @@ in anchors.verticalCenter: parent.verticalCenter text: "video" color: recCard.recording ? Theme.err - : recCard.selecting ? Theme.base0D : Theme.base05 + : recCard.selecting ? Theme.accent : Theme.base05 font.pixelSize: 15 } SText { @@ -6495,7 +6529,7 @@ in SIcon { anchors.verticalCenter: parent.verticalCenter text: "coffee" - color: caffCard.on ? Theme.base0D : Theme.base05 + color: caffCard.on ? Theme.accent : Theme.base05 font.pixelSize: 15 } SText { @@ -6862,7 +6896,7 @@ in SIcon { anchors.horizontalCenter: parent.horizontalCenter text: calPopup.weatherGlyph(modelData.code) - color: Theme.base0D + color: Theme.accent font.pixelSize: 16 } SText { @@ -6984,7 +7018,7 @@ in width: 3 radius: 1.5 height: 3 + lvl * 13 - color: Theme.base0D + color: Theme.accent opacity: 0.35 + lvl * 0.65 x: vinyl.width / 2 - width / 2 y: vinyl.height / 2 - vinyl.ringR - height @@ -7302,7 +7336,7 @@ in height: 16 trackH: 4 value: mediaCard.pwNode && mediaCard.pwNode.audio ? Math.min(1, mediaCard.pwNode.audio.volume) : 0 - fillColor: mediaCard.pwNode && mediaCard.pwNode.audio && mediaCard.pwNode.audio.muted ? Theme.base03 : Theme.base0D + fillColor: mediaCard.pwNode && mediaCard.pwNode.audio && mediaCard.pwNode.audio.muted ? Theme.base03 : Theme.accent onMoved: (v) => { if (mediaCard.pwNode && mediaCard.pwNode.audio) mediaCard.pwNode.audio.volume = v; } } @@ -7505,7 +7539,7 @@ in property bool shown: false property string icon: "volume-2" property real value: 0 - property color fill: Theme.base0D + property color fill: Theme.accent // Takes over the server glance's slot: fixed position, no // dependence on how tall the workspace column has grown. @@ -7547,7 +7581,7 @@ in show(a.muted ? "volume-x" : v >= 0.5 ? "volume-2" : v > 0 ? "volume-1" : "volume", - v, a.muted ? Theme.base03 : Theme.base0D); + v, a.muted ? Theme.base03 : Theme.accent); } Connections { @@ -7562,7 +7596,7 @@ in Connections { target: bar.shellRoot function onOsdBrightness(icon, value) { - osdItem.show(icon, value, Theme.base0D); + osdItem.show(icon, value, Theme.accent); } }