From d481be5f600a5687944567c7dddb81e56b70e454 Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Sun, 23 Nov 2025 20:23:31 +0100 Subject: [PATCH] nix --- Components/MaterialIcon.qml | 15 +++++ Daemons/Audio.qml | 110 ++++++++++++++++++++++++++++++++++++ Modules/AudioPopup.qml | 44 +++++++++++---- Modules/AudioWidget.qml | 11 +++- Modules/WindowTitle.qml | 1 + flake.nix | 48 ++++++++++++++++ nix/app2unit.nix | 14 +++++ nix/default.nix | 103 +++++++++++++++++++++++++++++++++ 8 files changed, 334 insertions(+), 12 deletions(-) create mode 100644 Components/MaterialIcon.qml create mode 100644 Daemons/Audio.qml create mode 100644 flake.nix create mode 100644 nix/app2unit.nix create mode 100644 nix/default.nix diff --git a/Components/MaterialIcon.qml b/Components/MaterialIcon.qml new file mode 100644 index 0000000..08a0792 --- /dev/null +++ b/Components/MaterialIcon.qml @@ -0,0 +1,15 @@ +import qs.Config + +CustomText { + property real fill + property int grade: DynamicColors.light ? 0 : -25 + + font.family: "Material Symbols Rounded" + font.pointSize: 15 + font.variableAxes: ({ + FILL: fill.toFixed(1), + GRAD: grade, + opsz: fontInfo.pixelSize, + wght: fontInfo.weight + }) +} diff --git a/Daemons/Audio.qml b/Daemons/Audio.qml new file mode 100644 index 0000000..5388eb2 --- /dev/null +++ b/Daemons/Audio.qml @@ -0,0 +1,110 @@ +pragma Singleton + +import qs.Config +import Caelestia.Services +import Quickshell +import Quickshell.Services.Pipewire +import QtQuick + +Singleton { + id: root + + property string previousSinkName: "" + property string previousSourceName: "" + + readonly property var nodes: Pipewire.nodes.values.reduce((acc, node) => { + if (!node.isStream) { + if (node.isSink) + acc.sinks.push(node); + else if (node.audio) + acc.sources.push(node); + } + return acc; + }, { + sources: [], + sinks: [] + }) + + readonly property list sinks: nodes.sinks + readonly property list sources: nodes.sources + + readonly property PwNode sink: Pipewire.defaultAudioSink + readonly property PwNode source: Pipewire.defaultAudioSource + + readonly property bool muted: !!sink?.audio?.muted + readonly property real volume: sink?.audio?.volume ?? 0 + + readonly property bool sourceMuted: !!source?.audio?.muted + readonly property real sourceVolume: source?.audio?.volume ?? 0 + + readonly property alias beatTracker: beatTracker + + function setVolume(newVolume: real): void { + if (sink?.ready && sink?.audio) { + sink.audio.muted = false; + sink.audio.volume = Math.max(0, Math.min(100, newVolume)); + } + } + + function incrementVolume(amount: real): void { + setVolume(volume + (amount || 5)); + } + + function decrementVolume(amount: real): void { + setVolume(volume - (amount || 5)); + } + + function setSourceVolume(newVolume: real): void { + if (source?.ready && source?.audio) { + source.audio.muted = false; + source.audio.volume = Math.max(0, Math.min(100, newVolume)); + } + } + + function incrementSourceVolume(amount: real): void { + setSourceVolume(sourceVolume + (amount || 5)); + } + + function decrementSourceVolume(amount: real): void { + setSourceVolume(sourceVolume - (amount || 5)); + } + + function setAudioSink(newSink: PwNode): void { + Pipewire.preferredDefaultAudioSink = newSink; + } + + function setAudioSource(newSource: PwNode): void { + Pipewire.preferredDefaultAudioSource = newSource; + } + + onSinkChanged: { + if (!sink?.ready) + return; + + const newSinkName = sink.description || sink.name || qsTr("Unknown Device"); + + previousSinkName = newSinkName; + } + + onSourceChanged: { + if (!source?.ready) + return; + + const newSourceName = source.description || source.name || qsTr("Unknown Device"); + + previousSourceName = newSourceName; + } + + Component.onCompleted: { + previousSinkName = sink?.description || sink?.name || qsTr("Unknown Device"); + previousSourceName = source?.description || source?.name || qsTr("Unknown Device"); + } + + PwObjectTracker { + objects: [...root.sinks, ...root.sources] + } + + BeatTracker { + id: beatTracker + } +} diff --git a/Modules/AudioPopup.qml b/Modules/AudioPopup.qml index 4c9ffec..1af0650 100644 --- a/Modules/AudioPopup.qml +++ b/Modules/AudioPopup.qml @@ -7,12 +7,36 @@ import QtQuick.Layouts import QtQuick.Controls import qs.Config import qs.Components +import qs.Daemons -Item { +CustomRect { id: root implicitWidth: layout.implicitWidth + 10 * 2 - implicitHeight: layout.implicitHeight + 10 * 2 + implicitHeight: 0 + color: Config.useDynamicColors ? DynamicColors.tPalette.m3surface : "#40000000" + clip: true + + property alias expanded: root.isExpanded + property bool isExpanded: false + + Anim { + id: expandAnim + running: root.isExpanded + target: root + property: "implicitHeight" + to: layout.implicitHeight + 10 * 2 + duration: MaterialEasing.standardTime + } + + Anim { + id: collapseAnim + running: !root.isExpanded + target: root + property: "implicitHeight" + to: 0 + duration: MaterialEasing.standardTime + } ButtonGroup { id: sinks @@ -58,7 +82,7 @@ Item { Repeater { model: Audio.sources - StyledRadioButton { + CustomRadioButton { required property PwNode modelData ButtonGroup.group: sources @@ -114,7 +138,7 @@ Item { color: DynamicColors.palette.m3onPrimaryContainer function onClicked(): void { - root.wrapper.hasCurrent = false; + root.isExpanded = !root.isExpanded; Quickshell.execDetached(["app2unit", "--", "pavucontrol"]); } } @@ -123,18 +147,18 @@ Item { id: expandBtn anchors.centerIn: parent - spacing: Appearance.spacing.small + spacing: 7 - StyledText { - Layout.leftMargin: Appearance.padding.smaller + CustomText { + Layout.leftMargin: 7 text: qsTr("Open settings") - color: Colours.palette.m3onPrimaryContainer + color: DynamicColors.palette.m3onPrimaryContainer } MaterialIcon { text: "chevron_right" - color: Colours.palette.m3onPrimaryContainer - font.pointSize: Appearance.font.size.large + color: DynamicColors.palette.m3onPrimaryContainer + font.pointSize: 18 } } } diff --git a/Modules/AudioWidget.qml b/Modules/AudioWidget.qml index 336b8c9..b5347c4 100644 --- a/Modules/AudioWidget.qml +++ b/Modules/AudioWidget.qml @@ -53,13 +53,20 @@ Item { border.width: 0 } + AudioPopup { + id: audioPopup + anchors.left: parent.left + anchors.top: parent.bottom + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onEntered: root.expanded = true - onExited: root.expanded = false + onEntered: audioPopup.expanded = true + onExited: audioPopup.expanded = false + RowLayout { anchors { fill: parent diff --git a/Modules/WindowTitle.qml b/Modules/WindowTitle.qml index ebe6f77..76b7c7a 100644 --- a/Modules/WindowTitle.qml +++ b/Modules/WindowTitle.qml @@ -41,6 +41,7 @@ Item { text: root.currentTitle color: root.textColor elide: Text.ElideRight + font.family: "Rubik" font.pixelSize: 16 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3d44654 --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + quickshell = { + url = "git+https://git.outfoxxed.me/outfoxxed/quickshell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + ... + } @ inputs: let + forAllSystems = fn: + nixpkgs.lib.genAttrs nixpkgs.lib.platforms.linux ( + system: fn nixpkgs.legacyPackages.${system} + ); + in { + formatter = forAllSystems (pkgs: pkgs.alejandra); + + packages = forAllSystems (pkgs: rec { + zshell = pkgs.callPackage ./nix { + rev = self.rev or self.dirtyRev; + stdenv = pkgs.clangStdenv; + quickshell = inputs.quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default.override { + withX11 = false; + withI3 = false; + }; + app2unit = pkgs.callPackage ./nix/app2unit.nix {inherit pkgs;}; + }; + debug = zshell.override {debug = true;}; + default = zshell; + }); + + devShells = forAllSystems (pkgs: { + default = let + shell = self.packages.${pkgs.stdenv.hostPlatform.system}.zshell; + in + pkgs.mkShell.override {stdenv = shell.stdenv;} { + inputsFrom = [shell shell.plugin shell.extras]; + packages = with pkgs; [clazy material-symbols rubik nerd-fonts.caskaydia-cove]; + CAELESTIA_XKB_RULES_PATH = "${pkgs.xkeyboard-config}/share/xkeyboard-config-2/rules/base.lst"; + }; + }); + }; +} diff --git a/nix/app2unit.nix b/nix/app2unit.nix new file mode 100644 index 0000000..51b4241 --- /dev/null +++ b/nix/app2unit.nix @@ -0,0 +1,14 @@ +{ + pkgs, # To ensure the nixpkgs version of app2unit + fetchFromGitHub, + ... +}: +pkgs.app2unit.overrideAttrs (final: prev: rec { + version = "1.0.3"; # Fix old issue related to missing env var + src = fetchFromGitHub { + owner = "Vladimir-csp"; + repo = "app2unit"; + tag = "v${version}"; + hash = "sha256-7eEVjgs+8k+/NLteSBKgn4gPaPLHC+3Uzlmz6XB0930="; + }; +}) diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 0000000..ca5d6a2 --- /dev/null +++ b/nix/default.nix @@ -0,0 +1,103 @@ +{ + rev, + lib, + stdenv, + makeWrapper, + makeFontsConf, + app2unit, + lm_sensors, + swappy, + wl-clipboard, + libqalculate, + bash, + hyprland, + material-symbols, + rubik, + nerd-fonts, + qt6, + quickshell, + aubio, + fftw, + pipewire, + xkeyboard-config, + cmake, + ninja, + pkg-config, + debug ? false, + withCli ? false, + extraRuntimeDeps ? [], +}: let + version = "1.0.0"; + + runtimeDeps = + [ + ddcutil + brightnessctl + app2unit + networkmanager + lm_sensors + swappy + wl-clipboard + libqalculate + bash + hyprland + ] + ++ extraRuntimeDeps + + fontconfig = makeFontsConf { + fontDirectories = [material-symbols rubik nerd-fonts.caskaydia-cove]; + }; + + cmakeBuildType = + if debug + then "Debug" + else "RelWithDebInfo"; + + plugin = stdenv.mkDerivation { + inherit cmakeBuildType; + name = "zshell-qml-plugin${lib.optionalString debug "-debug"}"; + src = lib.fileset.toSource { + root = ./..; + fileset = lib.fileset.union ./../CMakeLists.txt ./../Plugins; + }; + + nativeBuildInputs = [cmake ninja pkg-config]; + buildInputs = [qt6.qtbase qt6.qtdeclarative libqalculate pipewire aubio libcava fftw]; + + dontWrapQtApps = true; + }; +in + stdenv.mkDerivation { + inherit version cmakeBuildType; + pname = "zshell${lib.optionalString debug "-debug"}"; + src = ./..; + + nativeBuildInputs = [cmake ninja makeWrapper qt6.wrapQtAppsHook]; + buildInputs = [quickshell extras plugin xkeyboard-config qt6.qtbase]; + propagatedBuildInputs = runtimeDeps; + + cmakeFlags = + [ + (lib.cmakeFeature "ENABLE_MODULES" "shell") + (lib.cmakeFeature "INSTALL_QSCONFDIR" "${placeholder "out"}/share/zshell") + ] + ++ cmakeVersionFlags; + + dontStrip = debug; + + prePatch = '' + substituteInPlace assets/pam.d/fprint \ + --replace-fail pam_fprintd.so /run/current-system/sw/lib/security/pam_fprintd.so + substituteInPlace shell.qml \ + --replace-fail 'ShellRoot {' 'ShellRoot { settings.watchFiles: false' + ''; + + postInstall = '' + mkdir -p $out/lib + ln -s ${extras}/lib/* $out/lib/ + ''; + + passthru = { + inherit plugin; + }; + }