Compare commits

..

3 Commits

Author SHA1 Message Date
tux
352eb99568 style: format code 2026-09-11 02:10:39 +05:30
tux
f2268f65e7 feat(cava): update bar opacity using level 2026-09-04 22:56:32 +05:30
tux
6357b949b3 feat(bar): add smooth sliding highlight to workspaces 2026-09-04 22:55:22 +05:30
4 changed files with 224 additions and 145 deletions

111
flake.nix
View File

@@ -15,66 +15,63 @@
}; };
}; };
outputs = outputs = {
{ self,
self, nixpkgs,
nixpkgs, quickshell,
quickshell, treefmt-nix,
treefmt-nix, }: let
}: system = "x86_64-linux";
let pkgs = nixpkgs.legacyPackages.${system};
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
quickshellWithModules = quickshell.packages.${system}.default.withModules [ quickshellWithModules = quickshell.packages.${system}.default.withModules [
pkgs.qt6.qtbase pkgs.qt6.qtbase
pkgs.qt6.qtdeclarative pkgs.qt6.qtdeclarative
pkgs.qt6.qtmultimedia pkgs.qt6.qtmultimedia
pkgs.qt6.qttranslations pkgs.qt6.qttranslations
]; ];
treefmtEval = treefmt-nix.lib.evalModule pkgs { treefmtEval = treefmt-nix.lib.evalModule pkgs {
projectRootFile = "flake.nix"; projectRootFile = "flake.nix";
programs = { programs = {
nixfmt.enable = true; alejandra.enable = true;
qmlformat.enable = true; qmlformat.enable = true;
clang-format.enable = true; clang-format.enable = true;
};
};
in
rec {
formatter.${system} = treefmtEval.config.build.wrapper;
packages.${system} = {
tshell = pkgs.callPackage ./package.nix {
inherit quickshellWithModules;
};
default = packages.${system}.tshell;
};
devShells.${system}.default = pkgs.mkShell {
packages = [
quickshellWithModules
pkgs.cava
pkgs.wallust
pkgs.dbus
pkgs.cmake
pkgs.ninja
pkgs.qt6.qtbase
pkgs.qt6.qtdeclarative
];
shellHook = ''
if [ -d "$PWD/plugin/dist/lib/qt-6/qml" ]; then
export NIXPKGS_QT6_QML_IMPORT_PATH="$PWD/plugin/dist/lib/qt-6/qml''${NIXPKGS_QT6_QML_IMPORT_PATH:+:$NIXPKGS_QT6_QML_IMPORT_PATH}"
export QML2_IMPORT_PATH="$PWD/plugin/dist/lib/qt-6/qml''${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}"
else
echo "gpu plugin is not built; run from the repo root to enable 'import Tshell.Cardwire':"
echo " cmake -S plugin -B plugin/dist -DCMAKE_INSTALL_PREFIX=\$PWD/plugin/dist"
echo " cmake --build plugin/dist --target install"
fi
'';
}; };
}; };
in rec {
formatter.${system} = treefmtEval.config.build.wrapper;
packages.${system} = {
tshell = pkgs.callPackage ./package.nix {
inherit quickshellWithModules;
};
default = packages.${system}.tshell;
};
devShells.${system}.default = pkgs.mkShell {
packages = [
quickshellWithModules
pkgs.cava
pkgs.wallust
pkgs.dbus
pkgs.cmake
pkgs.ninja
pkgs.qt6.qtbase
pkgs.qt6.qtdeclarative
];
shellHook = ''
if [ -d "$PWD/plugin/dist/lib/qt-6/qml" ]; then
export NIXPKGS_QT6_QML_IMPORT_PATH="$PWD/plugin/dist/lib/qt-6/qml''${NIXPKGS_QT6_QML_IMPORT_PATH:+:$NIXPKGS_QT6_QML_IMPORT_PATH}"
export QML2_IMPORT_PATH="$PWD/plugin/dist/lib/qt-6/qml''${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}"
else
echo "gpu plugin is not built; run from the repo root to enable 'import Tshell.Cardwire':"
echo " cmake -S plugin -B plugin/dist -DCMAKE_INSTALL_PREFIX=\$PWD/plugin/dist"
echo " cmake --build plugin/dist --target install"
fi
'';
};
};
} }

View File

@@ -39,7 +39,7 @@ Rectangle {
radius: bar.width / 2 radius: bar.width / 2
color: Theme.colors.accent color: Theme.colors.accent
antialiasing: true antialiasing: true
opacity: 0.55 + 0.45 * bar.height / Math.max(1, barRow.height) opacity: 0.45 + (bar.level * 0.55)
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {

View File

@@ -4,57 +4,141 @@ import QtQuick
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.config import qs.config
Row { Item {
id: root id: root
property int count: 7 property int count: 7
spacing: Theme.spacing implicitWidth: pillsRow.implicitWidth
implicitHeight: Theme.workspaces.height
Behavior on implicitWidth {
NumberAnimation {
duration: Theme.duration
easing.type: Easing.OutQuint
}
}
Repeater { Row {
model: root.count id: pillsRow
Rectangle { anchors.fill: parent
id: pill spacing: Theme.spacing
required property int index Repeater {
property var ws: Hyprland.workspaces.values.find(w => w.id === pill.index + 1) model: root.count
property bool isActive: Hyprland.focusedWorkspace?.id === (pill.index + 1)
radius: Theme.workspaces.radius Rectangle {
implicitHeight: Theme.workspaces.height id: pill
implicitWidth: pill.isActive ? pill.implicitHeight * Theme.workspaces.activeWidthRatio : pill.implicitHeight
color: {
if (handler.hovered)
return Theme.colors.accent;
if (pill.isActive || pill.ws)
return Theme.colors.accent;
return Theme.colors.inActive;
}
MouseArea { required property int index
hoverEnabled: true property var ws: Hyprland.workspaces.values.find(w => w.id === pill.index + 1)
anchors.fill: parent property bool isActive: Hyprland.focusedWorkspace?.id === (pill.index + 1)
cursorShape: Qt.PointingHandCursor
onClicked: Hyprland.dispatch("workspace " + (pill.index + 1))
}
HoverHandler { z: 1
id: handler radius: Theme.workspaces.radius
} implicitHeight: Theme.workspaces.height
implicitWidth: pill.isActive ? pill.implicitHeight * Theme.workspaces.activeWidthRatio : pill.implicitHeight
Behavior on color { color: {
ColorAnimation { if (pill.isActive)
duration: Theme.duration return "transparent";
if (handler.hovered)
return Theme.colors.accent;
if (pill.ws)
return Theme.colors.secondaryForeground;
return Theme.colors.inActive;
} }
}
Behavior on implicitWidth { MouseArea {
NumberAnimation { hoverEnabled: true
duration: Theme.duration anchors.fill: parent
easing.type: Easing.OutQuad cursorShape: Qt.PointingHandCursor
onClicked: Hyprland.dispatch("hl.dsp.focus({ workspace = " + (pill.index + 1) + " })")
}
HoverHandler {
id: handler
}
Behavior on color {
ColorAnimation {
duration: Theme.duration + 50
}
}
Behavior on implicitWidth {
NumberAnimation {
duration: Theme.duration
easing.type: Easing.OutQuad
}
} }
} }
} }
} }
Rectangle {
id: highlight
z: 2
radius: Theme.workspaces.radius
color: Theme.colors.accent
readonly property real activeW: Theme.workspaces.height * Theme.workspaces.activeWidthRatio
readonly property real inactiveW: Theme.workspaces.height
property int curIdx: Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id - 1 : -1
property int prevIdx: 0
function getX(index, activeIndex) {
if (index < 0)
return 0;
let x = 0;
for (let i = 0; i < index; i++)
x += (i === activeIndex ? activeW : inactiveW) + pillsRow.spacing;
return x;
}
property real targetLeft: curIdx >= 0 ? getX(curIdx, curIdx) : 0
property real targetRight: curIdx >= 0 ? targetLeft + activeW : 0
property real actualLeft: targetLeft
property real actualRight: targetRight
Behavior on actualLeft {
NumberAnimation {
id: leftAnim
duration: Theme.duration
easing.type: Easing.OutQuint
}
}
Behavior on actualRight {
NumberAnimation {
id: rightAnim
duration: Theme.duration
easing.type: Easing.OutQuint
}
}
onCurIdxChanged: {
if (curIdx >= 0 && prevIdx >= 0) {
if (curIdx > prevIdx) {
leftAnim.duration = 400;
rightAnim.duration = 300;
} else if (curIdx < prevIdx) {
leftAnim.duration = 300;
rightAnim.duration = 400;
}
}
if (curIdx >= 0)
prevIdx = curIdx;
}
x: actualLeft
width: actualRight - actualLeft
height: parent.height
opacity: curIdx >= 0 ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: 180
}
}
}
} }

View File

@@ -10,73 +10,71 @@
cava, cava,
wallust, wallust,
nerd-fonts, nerd-fonts,
extraRuntimeDeps ? [ ], extraRuntimeDeps ? [],
}: }: let
let
version = "0.1.0"; version = "0.1.0";
runtimeDeps = [ runtimeDeps =
cava [
wallust cava
] wallust
++ extraRuntimeDeps; ]
++ extraRuntimeDeps;
fontconfig = makeFontsConf { fontconfig = makeFontsConf {
fontDirectories = [ nerd-fonts.fira-code ]; fontDirectories = [nerd-fonts.fira-code];
}; };
# Unlike lib.fileset, this keeps untracked files (the flake only sees # Unlike lib.fileset, this keeps untracked files (the flake only sees
# git-tracked paths) while dropping build artifacts. # git-tracked paths) while dropping build artifacts.
src = lib.cleanSourceWith { src = lib.cleanSourceWith {
src = lib.cleanSource ./.; src = lib.cleanSource ./.;
filter = filter = path: _: let
path: _: name = baseNameOf path;
let in
name = baseNameOf path;
in
!(name == "dist" || name == "result" || name == "result-bin" || lib.hasPrefix "build" name); !(name == "dist" || name == "result" || name == "result-bin" || lib.hasPrefix "build" name);
}; };
in in
stdenv.mkDerivation { stdenv.mkDerivation {
inherit version src; inherit version src;
pname = "tshell"; pname = "tshell";
dontWrapQtApps = true; dontWrapQtApps = true;
preConfigure = '' preConfigure = ''
cd plugin cd plugin
''; '';
nativeBuildInputs = [ nativeBuildInputs = [
cmake cmake
ninja ninja
makeWrapper makeWrapper
]; ];
buildInputs = [ buildInputs = [
qt6.qtbase qt6.qtbase
qt6.qtdeclarative qt6.qtdeclarative
]; ];
propagatedBuildInputs = runtimeDeps; propagatedBuildInputs = runtimeDeps;
postInstall = '' postInstall = ''
mkdir -p $out/bin $out/share/tshell mkdir -p $out/bin $out/share/tshell
for entry in shell.qml assets config modules services ui windows; do for entry in shell.qml assets config modules services ui windows; do
cp -r "$src/$entry" $out/share/tshell/ cp -r "$src/$entry" $out/share/tshell/
done done
makeWrapper ${quickshellWithModules}/bin/qs $out/bin/tshell \ makeWrapper ${quickshellWithModules}/bin/qs $out/bin/tshell \
--prefix PATH : "${lib.makeBinPath runtimeDeps}" \ --prefix PATH : "${lib.makeBinPath runtimeDeps}" \
--set FONTCONFIG_FILE "${fontconfig}" \ --set FONTCONFIG_FILE "${fontconfig}" \
--suffix NIXPKGS_QT6_QML_IMPORT_PATH : "$out/lib/qt-6/qml" \ --suffix NIXPKGS_QT6_QML_IMPORT_PATH : "$out/lib/qt-6/qml" \
--add-flags "-p $out/share/tshell" --add-flags "-p $out/share/tshell"
''; '';
meta = { meta = {
description = "tux's widgets for wayland"; description = "tux's widgets for wayland";
homepage = "https://github.com/tuxdotrs/tshell"; homepage = "https://github.com/tuxdotrs/tshell";
license = lib.licenses.gpl3Only; license = lib.licenses.gpl3Only;
mainProgram = "tshell"; mainProgram = "tshell";
platforms = lib.platforms.linux; platforms = lib.platforms.linux;
}; };
} }