From 5a3a18f9f7e81f22cc17a34d6b64662ebf8365e0 Mon Sep 17 00:00:00 2001 From: agryphus Date: Sun, 9 Feb 2025 21:34:23 -0500 Subject: [PATCH] Moved bar to quickshell --- misc/.config/hypr/hyprland.conf | 36 +++--- .../hyprland-autoname-workspaces/config.toml | 21 ++-- misc/.config/waybar/config | 6 +- misc/.config/waybar/style.css | 7 +- misc/.local/bin/startw | 19 +--- nixos/.config/nixos/core.nix | 70 +++++------- nixos/.config/nixos/derivations/ags.nix | 103 ------------------ nixos/.config/nixos/profiles/wm/hyprland.nix | 51 ++++++--- quickshell/.config/quickshell/Globals.qml | 18 +++ .../.config/quickshell/PopupContext.qml | 6 + quickshell/.config/quickshell/bar/Bar.qml | 57 ++++++++++ .../.config/quickshell/bar/BarBlock.qml | 89 +++++++++++++++ quickshell/.config/quickshell/bar/BarText.qml | 39 +++++++ .../.config/quickshell/bar/Notification.qml | 11 ++ .../quickshell/bar/NotificationPanel.qml | 101 +++++++++++++++++ quickshell/.config/quickshell/bar/Tooltip.qml | 89 +++++++++++++++ .../quickshell/bar/blocks/ActiveWorkspace.qml | 33 ++++++ .../.config/quickshell/bar/blocks/Battery.qml | 26 +++++ .../.config/quickshell/bar/blocks/CPU.qml | 37 +++++++ .../.config/quickshell/bar/blocks/Date.qml | 8 ++ .../quickshell/bar/blocks/Datetime.qml | 31 ++++++ .../.config/quickshell/bar/blocks/Icon.qml | 11 ++ .../.config/quickshell/bar/blocks/Memory.qml | 29 +++++ .../quickshell/bar/blocks/Notifications.qml | 34 ++++++ .../.config/quickshell/bar/blocks/Sound.qml | 15 +++ .../quickshell/bar/blocks/SystemTray.qml | 74 +++++++++++++ .../.config/quickshell/bar/blocks/Test.qml | 77 +++++++++++++ .../.config/quickshell/bar/blocks/Time.qml | 8 ++ .../quickshell/bar/blocks/Workspaces.qml | 63 +++++++++++ quickshell/.config/quickshell/shell.qml | 8 ++ 30 files changed, 971 insertions(+), 206 deletions(-) delete mode 100644 nixos/.config/nixos/derivations/ags.nix create mode 100644 quickshell/.config/quickshell/Globals.qml create mode 100644 quickshell/.config/quickshell/PopupContext.qml create mode 100644 quickshell/.config/quickshell/bar/Bar.qml create mode 100644 quickshell/.config/quickshell/bar/BarBlock.qml create mode 100644 quickshell/.config/quickshell/bar/BarText.qml create mode 100644 quickshell/.config/quickshell/bar/Notification.qml create mode 100644 quickshell/.config/quickshell/bar/NotificationPanel.qml create mode 100644 quickshell/.config/quickshell/bar/Tooltip.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/ActiveWorkspace.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Battery.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/CPU.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Date.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Datetime.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Icon.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Memory.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Notifications.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Sound.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/SystemTray.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Test.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Time.qml create mode 100644 quickshell/.config/quickshell/bar/blocks/Workspaces.qml create mode 100644 quickshell/.config/quickshell/shell.qml diff --git a/misc/.config/hypr/hyprland.conf b/misc/.config/hypr/hyprland.conf index d728518..467a3f9 100644 --- a/misc/.config/hypr/hyprland.conf +++ b/misc/.config/hypr/hyprland.conf @@ -3,7 +3,7 @@ # Wayland related environment variables. env = HYPRCURSOR_THEME,McMojave env = HYPRCURSOR_SIZE,30 -env = TERMINAL,foot +env = TERMINAL,st env = QT_QPA_PLATFORM,wayland opengl { @@ -37,7 +37,7 @@ input { sensitivity = 0 # -1.0 - 1.0, 0 means no modification. - kb_options = caps:swapescape + # kb_options = caps:swapescape } general { @@ -112,6 +112,7 @@ windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1] windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1] windowrulev2 = bordersize 0, floating:0, onworkspace:f[1] windowrulev2 = rounding 0, floating:0, onworkspace:f[1] +windowrulev2 = float, class:nm-tray gestures { # See https://wiki.hyprland.org/Configuring/Variables/ for more @@ -122,7 +123,7 @@ misc { # See https://wiki.hyprland.org/Configuring/Variables/ for more force_default_wallpaper = 0 # Set to 0 to disable the anime mascot wallpapers # enable_swallow = true - # swallow_regex = ^(st|foot|footclient)$ + # swallow_regex = ^(st-256color)$ mouse_move_enables_dpms = true } @@ -140,29 +141,28 @@ layerrule = ignorezero, waybar $mainMod = SUPER # Program spawning hotkeys -bind = CONTROL ALT, BACKSPACE, exec, foot -e zsh -c 'btop' +bind = CONTROL ALT, BACKSPACE, exec, st -e zsh -c 'btop' bind = $mainMod CONTROL, W, exec, [floating] foot -W 78x38 -e nmtui -bind = $mainMod, RETURN, exec, foot -bind = $mainMod, W, exec, firefox -bind = $mainMod, E, exec, foot -e zsh -c 'tmp="$(mktemp -t "yazi-cwd.XXXXX")"; yazi "$@" --cwd-file="$tmp"; printf "\033]0;foot\007"; if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then; cd -- "$cwd"; fi; rm -f -- "$tmp"; exec $SHELL' +bind = $mainMod, RETURN, exec, st +bind = $mainMod, W, exec, zen +bind = $mainMod, E, exec, st -e zsh -c 'tmp="$(mktemp -t "yazi-cwd.XXXXX")"; yazi "$@" --cwd-file="$tmp"; printf "\033]0;st\007"; if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then; cd -- "$cwd"; fi; rm -f -- "$tmp"; exec $SHELL' bind = $mainMod SHIFT, E, exec, emacsclient -c -a 'emacs' -bind = $mainMod, C, exec, foot -e zsh -c 'khal interactive' -bind = $mainMod, M, exec, foot -e zsh -c 'neomutt' +bind = $mainMod, C, exec, st -e zsh -c 'khal interactive' +bind = $mainMod, M, exec, st -e zsh -c 'neomutt' # Menu hoykeys bind = $mainMod, R, exec, fuzzel # System control keybinds -binde = $mainMod, MINUS, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 1%-; test -z "$(pidof waybar)" && notify-send -r 44 "$(wpctl get-volume @DEFAULT_AUDIO_SINK@)" -binde = $mainMod SHIFT, MINUS, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-; test -z "$(pidof waybar)" && notify-send -r 44 "$(wpctl get-volume @DEFAULT_AUDIO_SINK@)" -binde = $mainMod, EQUAL, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 1%+; test -z "$(pidof waybar)" && notify-send -r 44 "$(wpctl get-volume @DEFAULT_AUDIO_SINK@)" -binde = $mainMod SHIFT, EQUAL, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+; test -z "$(pidof waybar)" && notify-send -r 44 "$(wpctl get-volume @DEFAULT_AUDIO_SINK@)" +binde = $mainMod, MINUS, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 1%- +binde = $mainMod SHIFT, MINUS, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- +binde = $mainMod, EQUAL, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 1%+ +binde = $mainMod SHIFT, EQUAL, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ binde = ,F7, exec, change-brightness up binde = ,XF86MonBrightnessUp, exec, change-brightness up binde = ,F6, exec, change-brightness down binde = ,XF86MonBrightnessDown,exec, change-brightness down -bind = $mainMod, L, exec, swaylock -e -c 000000 -bind = $mainMod, L, exec, sleep 5; hyprctl dispatch dpms off +bind = $mainMod, L, exec, hyprlock bind = $mainMod CONTROL SHIFT, R, exec, startw # Reload all graphical daemons @@ -176,7 +176,9 @@ bind = $mainMod, D, exec, hyprctl dispatch submap menu-submap; sleep 2; hyprctl submap = menu-submap bind = ,A, exec, menu-accent bind = ,A, submap, reset -bind = ,M, exec, menu-man +bind = ,B, exec, [floating] foot -W 80x17 -e dash -c -i 'menu-bookmark' +bind = ,B, submap, reset +bind = ,M, exec, [floating] foot -W 78x38 -e dash -c -i 'menu-man' bind = ,M, submap, reset bind = ,W, exec, networkmanager_dmenu bind = ,W, submap, reset @@ -186,7 +188,7 @@ bind = ,I, exec, menu-wpio source bind = ,I, submap, reset bind = ,L, exec, hyprmonitors menu bind = ,L, submap, reset -bind = ,P, exec, menu-pass +bind = ,P, exec, [floating] foot -W 80x17 -e zsh -c -i 'menu-pass' bind = ,P, submap, reset submap = reset diff --git a/misc/.config/hyprland-autoname-workspaces/config.toml b/misc/.config/hyprland-autoname-workspaces/config.toml index 65b820f..058ccbc 100644 --- a/misc/.config/hyprland-autoname-workspaces/config.toml +++ b/misc/.config/hyprland-autoname-workspaces/config.toml @@ -1,29 +1,34 @@ version = "1.1.15" +[title."(?i)foot"] +Yazi = "󰇥" + [class] DEFAULT = "{class}" -"(?i)Anki" = " " +"(?i)anki" = " " "(?i)blueman-manager-wrapped" = " " "(?i)chromium" = " " "(?i)discord" = "󰊴 " -"(?i)Element" = "󰘨 " +"(?i)element" = " " "(?i)emacs" = " " "(?i)firefox" = " " "(?i)foot" = " " "(?i)ghidra" = " " -"(?i)Gimp" = " " -"(?i)Kitty" = " " +"(?i)gimp" = " " +"(?i)kitty" = " " "(?i)libreoffice-draw" = "󰽉 " "(?i)libreoffice-writer" = "󰷈 " "(?i)mpv" = " " "(?i)mullvad vpn" = " " -"(?i)Nsxiv" = " " +"(?i)nsxiv" = " " +"(?i)signal" = "󰭹 " "(?i)slack" = "󰒱 " "(?i)spotify" = " " "(?i)st" = " " "(?i)steam" = " " "(?i)virtualbox" = " " "(?i)zathura" = " " +"(?i)zen" = "󰇧 " [class_active] @@ -52,7 +57,7 @@ DEFAULT = "{class}" "(?i)neomutt" = "neomutt" [title_in_class_active."(?i)firefox"] -"(?i)twitch" = "{icon}" +"(?i)twitch" = "{icon}" [title_in_initial_class] @@ -79,9 +84,9 @@ dedup_inactive_fullscreen = false delim = "" workspace = "{id}:{delim}{clients}" workspace_empty = "{id}" -client = "{icon}" +client = "{icon}" client_fullscreen = "[{icon}]" -client_active = "{icon}" +client_active = "{icon}" client_dup = "{icon}{counter_sup}" client_dup_active = "*{icon}*{delim}{icon}{counter_unfocused_sup}" client_dup_fullscreen = "[{icon}]{delim}{icon}{counter_unfocused_sup}" diff --git a/misc/.config/waybar/config b/misc/.config/waybar/config index 430c2d0..bc7ec94 100644 --- a/misc/.config/waybar/config +++ b/misc/.config/waybar/config @@ -3,10 +3,14 @@ "position": "top", "height": 27, - "modules-left": ["hyprland/workspaces", "custom/scratch", "hyprland/mode", "hyprland/window"], + "modules-left": ["custom/icon", "hyprland/workspaces", "custom/scratch", "hyprland/mode", "hyprland/window"], "modules-center": [], "modules-right": ["tray", "memory", "pulseaudio", "battery", "clock"], + "custom/icon": { + "format": "", + }, + "hyprland/workspaces": { "disable-scroll": true, "disable-markup": true, diff --git a/misc/.config/waybar/style.css b/misc/.config/waybar/style.css index ff09087..7e9fe40 100644 --- a/misc/.config/waybar/style.css +++ b/misc/.config/waybar/style.css @@ -29,9 +29,12 @@ button:hover { } -#custom-scratch { - color: #b8b8b8; +#custom-icon { + color: #ffffff; padding: 0px 9px 0px 9px; + margin: 0px 0px 0px 0px; + border: none; + border-radius: 0; } #workspaces button { diff --git a/misc/.local/bin/startw b/misc/.local/bin/startw index c48e77d..058d0a6 100755 --- a/misc/.local/bin/startw +++ b/misc/.local/bin/startw @@ -5,24 +5,11 @@ swww kill pkill fcitx5 -# Start daemons that can turn on asynchronously -( - foot --server & - systemctl --user restart network-manager-applet - pkill emacs; emacs --daemon -) & - -# Rearrange monitors -hyprmonitors auto - systemctl --user restart hyprland-autoname-workspaces swww-daemon & fcitx5 & -waybar & - -# Sometimes the monitor config does not want to apply properly on the first -# go. I then have to reload the configuration to make sure that the monitors -# are actually in their intended place. -hyprctl reload +quickshell & +hypridle & +pkill emacs; emacs --daemo diff --git a/nixos/.config/nixos/core.nix b/nixos/.config/nixos/core.nix index 72e09be..2c4359d 100644 --- a/nixos/.config/nixos/core.nix +++ b/nixos/.config/nixos/core.nix @@ -1,8 +1,6 @@ { config, pkgs, ... }: let - # # Only allowing unfree for a useUasm yazi fix. Remove once patched. - # nixos-unstable = (import {config.allowUnfree = true;}); flake-compat = builtins.fetchTarball "https://github.com/edolstra/flake-compat/archive/master.tar.gz"; in { # Nix settings @@ -31,17 +29,17 @@ in { ]; }; - i18n.defaultLocale = "en_US.UTF-8"; + i18n.defaultLocale = "en_US.UTF-8"; i18n.extraLocaleSettings = { - LC_ADDRESS = "en_US.UTF-8"; + LC_ADDRESS = "en_US.UTF-8"; LC_IDENTIFICATION = "en_US.UTF-8"; - LC_MEASUREMENT = "en_US.UTF-8"; - LC_MONETARY = "en_US.UTF-8"; - LC_NAME = "en_US.UTF-8"; - LC_NUMERIC = "en_US.UTF-8"; - LC_PAPER = "en_US.UTF-8"; - LC_TELEPHONE = "en_US.UTF-8"; - LC_TIME = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; }; fonts.packages = with pkgs; [ @@ -150,7 +148,6 @@ in { openconnect # Connect to VPNs pass-nodmenu # CLI password store (without dmenu dependency) pinentry-curses # Terminal-based pinentry program - python311 # Python socat # Interact with sockets stow # Simlink farm (used for dotfile management) tldr # Brief info about a command @@ -179,10 +176,16 @@ in { nix-prefetch-git # Like nix-prefetch-url, but for git nvd # See diffs between builds - # Pop into an environment abiding by the Filesystem Hierarchy Standard to run - # applications which do not play nicely with NixOS. - ( - let + # Custom overlays. See below for explanations + fhs-run + python-common + ]; + + + nixpkgs.overlays = [ + (self: super: { + # Pop into an environment abiding by the Filesystem Hierarchy Standard + # to run applications which do not play nicely with NixOS. fhs-run = pkgs.buildFHSUserEnv { name = "fhs-run"; targetPkgs = pkgs: []; @@ -192,35 +195,14 @@ in { eval "$@" # Execute whatever arguments ''; }; - in - fhs-run - ) - # Defining an environment to run "make" with the proper libraries installed - # "make", in the main environment, references the script, which envokes the - # environment, and passes the args to gnumake. - ( - let - make-shell = pkgs.buildEnv { - name = "make-shell"; - paths = with pkgs; [ - # Tools - gnumake - pkg-config - - # Libraries - harfbuzz - xorg.libX11.dev - xorg.libXft - xorg.libXinerama - ]; - }; - in - (pkgs.writeScriptBin "make" '' - #!/usr/bin/env sh - nix-shell -p ${make-shell} --run "make $*" - '') - ) + # When evoking the command `python` from outside a shell, it runs the + # commands inside a nix shell containing common python packages that I + # always want to be available. + python-common = pkgs.python3.withPackages (ps: with ps; [ + requests + ]); + }) ]; nixpkgs.config.packageOverrides = pkgs: { diff --git a/nixos/.config/nixos/derivations/ags.nix b/nixos/.config/nixos/derivations/ags.nix deleted file mode 100644 index 6a9ad53..0000000 --- a/nixos/.config/nixos/derivations/ags.nix +++ /dev/null @@ -1,103 +0,0 @@ -{ lib -, stdenv -, buildNpmPackage -, fetchFromGitLab -, nodePackages -, meson -, pkg-config -, ninja -, gobject-introspection -, gtk3 -, libpulseaudio -, gjs -, wrapGAppsHook -, upower -, gnome -, gtk-layer-shell -, glib-networking -, networkmanager -, libdbusmenu-gtk3 -, gvfs -, libsoup_3 -, libnotify -, pam -, extraPackages ? [ ] -, version ? "git" -, buildTypes ? false -}: - -let - gvc-src = fetchFromGitLab { - domain = "gitlab.gnome.org"; - owner = "GNOME"; - repo = "libgnome-volume-control"; - rev = "8e7a5a4c3e51007ce6579292642517e3d3eb9c50"; - sha256 = "sha256-FosJwgTCp6/EI6WVbJhPisokRBA6oT0eo7d+Ya7fFX8="; - }; -in -stdenv.mkDerivation rec { - pname = "ags"; - inherit version; - - src = buildNpmPackage { - name = pname; - src = ../.; - - dontBuild = true; - - npmDepsHash = "sha256-ucWdADdMqAdLXQYKGOXHNRNM9bhjKX4vkMcQ8q/GZ20="; - - installPhase = '' - mkdir $out - cp -r * $out - ''; - }; - - mesonFlags = builtins.concatLists [ - (lib.optional buildTypes "-Dbuild_types=true") - ]; - - prePatch = '' - mkdir -p ./subprojects/gvc - cp -r ${gvc-src}/* ./subprojects/gvc - ''; - - postPatch = '' - chmod +x post_install.sh - patchShebangs post_install.sh - ''; - - nativeBuildInputs = [ - pkg-config - meson - ninja - nodePackages.typescript - wrapGAppsHook - gobject-introspection - ]; - - buildInputs = [ - gjs - gtk3 - libpulseaudio - upower - gnome.gnome-bluetooth - gtk-layer-shell - glib-networking - networkmanager - libdbusmenu-gtk3 - gvfs - libsoup_3 - libnotify - pam - ] ++ extraPackages; - - meta = with lib; { - description = "A customizable and extensible shell"; - homepage = "https://github.com/Aylur/ags"; - platforms = [ "x86_64-linux" "aarch64-linux" ]; - license = licenses.gpl3; - meta.maintainers = [ lib.maintainers.Aylur ]; - }; -} - diff --git a/nixos/.config/nixos/profiles/wm/hyprland.nix b/nixos/.config/nixos/profiles/wm/hyprland.nix index afc724b..82a9ace 100644 --- a/nixos/.config/nixos/profiles/wm/hyprland.nix +++ b/nixos/.config/nixos/profiles/wm/hyprland.nix @@ -6,6 +6,7 @@ let flake-compat = builtins.fetchTarball "https://github.com/edolstra/flake-compat/archive/master.tar.gz"; hyprland_nightly = (import flake-compat { src = builtins.fetchGit { + ref = "main"; url = "https://github.com/hyprwm/Hyprland.git"; submodules = true; }; @@ -24,11 +25,10 @@ in { hyprland = { # Dynamic tiling window manager enable = true; xwayland.enable = true; - package = nixos-unstable.hyprland.override(o: { - aquamarine = nixos-unstable.aquamarine; - }); + # package = nixos-unstable.hyprland; # package = hyprland_nightly.packages.${pkgs.stdenv.hostPlatform.system}.hyprland; }; + nm-applet.enable = true; }; systemd.user.services = { @@ -41,16 +41,16 @@ in { serviceConfig.Restart = "always"; serviceConfig.RestartSec = 1; }; - network-manager-applet = { - description = "Start the network manager applet"; - after = [ "graphical-session.target" ]; - requires = [ "graphical-session.target" ]; - wantedBy = [ "graphical-session.target" ]; - serviceConfig.Type = "forking"; - serviceConfig.Restart = "always"; - serviceConfig.RestartSec = 1; - serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet"; - }; + # network-manager-applet = { + # description = "Start the network manager applet"; + # after = [ "graphical-session.target" ]; + # requires = [ "graphical-session.target" ]; + # wantedBy = [ "graphical-session.target" ]; + # serviceConfig.Type = "forking"; + # serviceConfig.Restart = "always"; + # serviceConfig.RestartSec = 1; + # serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet"; + # }; }; environment.systemPackages = with pkgs; [ @@ -63,6 +63,7 @@ in { grimblast # Allows freezing screen grim # Screenshot tool hicolor-icon-theme # Icons + hypridle # Do commands upon user idle hyprland-autoname-workspaces # Add icons to workspace titles hyprlock # Screen locking utility hyprpaper @@ -70,7 +71,7 @@ in { kanshi # Autorandr substitute libnotify # Send messages to notification daemon libreoffice # MSOffice btfo - networkmanagerapplet # Wifi dropdown menu + # networkmanagerapplet # Wifi dropdown menu networkmanager_dmenu # Manage wifi with dmenu nsxiv # Image viewer nwg-displays @@ -80,6 +81,7 @@ in { rofi-pass # Rofi frontend for password store sassc # SCSS interpreter slurp # Screen selection utility + st swaylock # Wayland session locker swww # Sets background images texlive.combined.scheme-full # LaTeX to create documents @@ -94,6 +96,7 @@ in { xwaylandvideobridge # Allows screensharing from XWayland programs xorg.xcursorthemes zathura # Minimalist PDF reader + zen-browser # GTK Themes lxappearance-gtk2 # Theme switcher @@ -101,10 +104,28 @@ in { ]; nixpkgs.overlays = [ - (self: super: { + (final: prev: { hyprland-autoname-workspaces = nixos-unstable.hyprland-autoname-workspaces; waybar = nixos-unstable.waybar; typst = nixos-unstable.typst; + # typst = (import flake-compat { + # src = builtins.fetchGit { + # url = "https://github.com/typst/typst.git"; + # }; + # }).outputs.packages.${pkgs.stdenv.hostPlatform.system}.default; + zen-browser = (import flake-compat { + src = builtins.fetchGit { + url = "https://github.com/0xc000022070/zen-browser-flake.git"; + }; + }).outputs.packages.${pkgs.stdenv.hostPlatform.system}.default; + st = prev.st.overrideAttrs (o: { + src = /home/vince/.config/st; + buildInputs = o.buildInputs ++ (with pkgs; [ + # Extra libraries needed to build patches + harfbuzz + imlib2 + ]); + }); }) ]; } diff --git a/quickshell/.config/quickshell/Globals.qml b/quickshell/.config/quickshell/Globals.qml new file mode 100644 index 0000000..8c4a5af --- /dev/null +++ b/quickshell/.config/quickshell/Globals.qml @@ -0,0 +1,18 @@ +pragma Singleton + +import QtQuick +import Quickshell + +Singleton { + id: root + property var popupContext: PopupContext {}; + property var date: new Date() + + Timer { + interval: 1000 + repeat: true + running: true + + onTriggered: root.date = new Date() + } +} diff --git a/quickshell/.config/quickshell/PopupContext.qml b/quickshell/.config/quickshell/PopupContext.qml new file mode 100644 index 0000000..6f007c8 --- /dev/null +++ b/quickshell/.config/quickshell/PopupContext.qml @@ -0,0 +1,6 @@ +import QtQuick + +// Tracks which popup of a set is active. +QtObject { + property var popup: null; +} diff --git a/quickshell/.config/quickshell/bar/Bar.qml b/quickshell/.config/quickshell/bar/Bar.qml new file mode 100644 index 0000000..48a2bfa --- /dev/null +++ b/quickshell/.config/quickshell/bar/Bar.qml @@ -0,0 +1,57 @@ +import Quickshell +import Quickshell.Io +import QtQuick +import QtQuick.Layouts +import "blocks" as Blocks + +Scope { + Variants { + model: Quickshell.screens + + PanelWindow { + property var modelData + screen: modelData + + color: "#cc000000" + height: 27 + + anchors { + top: true + left: true + right: true + } + + RowLayout { + spacing: 0 + width: parent.width + height: parent.height + + // Left side + RowLayout { + spacing: 0 + Layout.alignment: Qt.AlignLeft + + Blocks.Icon {} + Blocks.Workspaces {} + Blocks.ActiveWorkspace {} + } + + // Right side + RowLayout { + spacing: 0 + Layout.alignment: Qt.AlignRight + + Blocks.SystemTray {} + Blocks.Test {} + Blocks.Notifications {} + Blocks.Memory {} + Blocks.Sound {} + Blocks.Battery {} + Blocks.Date {} + Blocks.Time {} + } + } + } + } +} + diff --git a/quickshell/.config/quickshell/bar/BarBlock.qml b/quickshell/.config/quickshell/bar/BarBlock.qml new file mode 100644 index 0000000..9daf98a --- /dev/null +++ b/quickshell/.config/quickshell/bar/BarBlock.qml @@ -0,0 +1,89 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell + +Rectangle { + id: root + Layout.preferredWidth: wsText.implicitWidth + 10 + Layout.preferredHeight: 27 + + property string text + property bool dim: false + property bool underline + property var onClicked: function() {} + property int leftPadding + property int rightPadding + + property string fgColor: "white" + property string dimFgColor: "#999999" + property string hoveredBgColor: "#444444" + + // Background color + color: { + if (mouseArea.containsMouse) + return hoveredBgColor; + return "transparent"; + } + + states: [ + State { + when: mouseArea.containsMouse + PropertyChanges { + target: root + } + } + ] + + Behavior on color { + ColorAnimation { + duration: 200 + } + } + + BarText { + id: wsText + text: root.text + anchors { + left: parent.left + right: parent.right + leftMargin: root.leftPadding + rightMargin: root.rightPadding + verticalCenter: parent.verticalCenter + } + + color: { + if (mouseArea.containsMouse || !root.dim) + return fgColor + return dimFgColor + } + + Behavior on color { + ColorAnimation { + duration: 100 + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: root + hoverEnabled: true + acceptedButtons: Qt.LeftButton + onClicked: root.onClicked() + } + + // While line underneath workspace + Rectangle { + id: wsLine + width: parent.width + height: 3 + + color: { + if (parent.underline) + return fgColor; + return "transparent"; + } + anchors.bottom: parent.bottom + } +} + diff --git a/quickshell/.config/quickshell/bar/BarText.qml b/quickshell/.config/quickshell/bar/BarText.qml new file mode 100644 index 0000000..25e2299 --- /dev/null +++ b/quickshell/.config/quickshell/bar/BarText.qml @@ -0,0 +1,39 @@ +import Quickshell +import Quickshell.Io +import QtQuick + +Item { + property string text + property string color: "white" + property string mainFont: "FiraCode" + property string symbolFont: "Symbols Nerd Font Mono" + property int pointSize: 11 + + implicitWidth: thetext.implicitWidth + implicitHeight: thetext.implicitHeight + + function wrapSymbols(text) { + const isSymbol = (codePoint) => + (codePoint >= 0xE000 && codePoint <= 0xF8FF) // Private Use Area + || (codePoint >= 0xF0000 && codePoint <= 0xFFFFF) // Supplementary Private Use Area-A + || (codePoint >= 0x100000 && codePoint <= 0x10FFFF); // Supplementary Private Use Area-B + + return text.replace(/./gu, (c) => isSymbol(c.codePointAt(0)) + ? `${c}` + : c); + } + + Text { + id: thetext + text: wrapSymbols(parent.text) + color: parent.color + anchors.centerIn: parent + + font { + family: parent.mainFont + pointSize: parent.pointSize + } + textFormat: Text.RichText + } +} + diff --git a/quickshell/.config/quickshell/bar/Notification.qml b/quickshell/.config/quickshell/bar/Notification.qml new file mode 100644 index 0000000..b86a966 --- /dev/null +++ b/quickshell/.config/quickshell/bar/Notification.qml @@ -0,0 +1,11 @@ +import QtQuick + +Text { + required property int id + required property string body + required property string summary + property int margin + + text: `- ${summary}: ${body}` +} + diff --git a/quickshell/.config/quickshell/bar/NotificationPanel.qml b/quickshell/.config/quickshell/bar/NotificationPanel.qml new file mode 100644 index 0000000..0ef8712 --- /dev/null +++ b/quickshell/.config/quickshell/bar/NotificationPanel.qml @@ -0,0 +1,101 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import Quickshell.Services.Notifications + +PanelWindow { + // required property font custom_font + required property color text_color + property list notification_objects + + width: 500 + height: 600 + + color: "#171a18" + + WlrLayershell.layer: WlrLayer.Overlay + + Rectangle { + border.width: 5 + border.color: "#8ec07c" + anchors.fill: parent + color: "transparent" + + ColumnLayout { + id: content + anchors { + left: parent.left + leftMargin: 10 + right: parent.right + rightMargin: 10 + top: parent.top + topMargin: 10 + } + + RowLayout { + Layout.fillWidth: true + + Text { + Layout.fillWidth: true + text: "Notifications:" + // font: custom_font + color: text_color + } + + Text { + text: "clear" + // font: custom_font + color: text_color + + TapHandler { + id: tapHandler + gesturePolicy: TapHandler.ReleaseWithinBounds + onTapped: { + server.trackedNotifications.values.forEach((notification) => { + notification.tracked = false + }) + notification_objects.forEach((object) => { + object.destroy(); + }) + notification_objects = []; + } + } + + HoverHandler { + id: mouse + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + cursorShape: Qt.PointingHandCursor + } + } + } + } + } + + NotificationServer { + id: server + onNotification: (notification) => { + notification.tracked = true + console.log(JSON.stringify(notification)); + var notification_component = Qt.createComponent("Notification.qml"); + var notification_object = notification_component + .createObject(content, + { + id: notification.id, + body: notification.body, + summary: notification.summary, + // font: custom_font, + color: text_color, + margin: 10 + } + ) + if (notification_object == null) { + console.log("Error creating notification") + } else { + notification_objects.push(notification_object); + } + } + } +} + diff --git a/quickshell/.config/quickshell/bar/Tooltip.qml b/quickshell/.config/quickshell/bar/Tooltip.qml new file mode 100644 index 0000000..7ab247d --- /dev/null +++ b/quickshell/.config/quickshell/bar/Tooltip.qml @@ -0,0 +1,89 @@ +import QtQuick +import Quickshell +import "root:/" // for Globals + +LazyLoader { + id: root + + // The item to display the tooltip at. If set to null the tooltip will be hidden. + property Item relativeItem: null + + // Tracks the item after relativeItem is unset. + property Item displayItem: null + + property PopupContext popupContext: Globals.popupContext + + property bool hoverable: false; + readonly property bool hovered: item?.hovered ?? false + + // The content to show in the tooltip. + required default property Component contentDelegate + + active: displayItem != null && popupContext.popup == this + + onRelativeItemChanged: { + if (relativeItem == null) { + if (item != null) item.hideTimer.start(); + } else { + if (item != null) item.hideTimer.stop(); + displayItem = relativeItem; + popupContext.popup = this; + } + } + + PopupWindow { + anchor { + window: root.displayItem.QsWindow.window + rect.y: anchor.window.height + 3 + rect.x: anchor.window.contentItem.mapFromItem(root.displayItem, root.displayItem.width / 2, 0).x + edges: Edges.Top + gravity: Edges.Bottom + } + + visible: true + + property alias hovered: body.containsMouse; + + property Timer hideTimer: Timer { + interval: 250 + + // unloads the popup by causing active to become false + onTriggered: root.popupContext.popup = null; + } + + color: "transparent" + + // don't accept mouse input if !hoverable + Region { id: emptyRegion } + mask: root.hoverable ? null : emptyRegion + + width: body.implicitWidth + height: body.implicitHeight + + MouseArea { + id: body + + anchors.fill: parent + implicitWidth: content.implicitWidth + 10 + implicitHeight: content.implicitHeight + 10 + + hoverEnabled: root.hoverable + + Rectangle { + anchors.fill: parent + + radius: 5 + border.width: 1 + color: palette.active.toolTipBase + border.color: palette.active.light + + Loader { + id: content + anchors.centerIn: parent + sourceComponent: contentDelegate + active: true + } + } + } + } +} diff --git a/quickshell/.config/quickshell/bar/blocks/ActiveWorkspace.qml b/quickshell/.config/quickshell/bar/blocks/ActiveWorkspace.qml new file mode 100644 index 0000000..22091f5 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/ActiveWorkspace.qml @@ -0,0 +1,33 @@ +import QtQuick +import Quickshell.Io +import Quickshell.Hyprland +import "../" + +BarText { + text: { + var str = activeWindowTitle + return str.length > chopLength ? str.slice(0, chopLength) + '...' : str; + } + + property int chopLength: 70 + property string activeWindowTitle + + Process { + id: titleProc + command: ["sh", "-c", "hyprctl activewindow | grep title: | sed 's/^[^:]*: //'"] + running: true + + stdout: SplitParser { + onRead: data => activeWindowTitle = data + } + } + + Component.onCompleted: { + Hyprland.rawEvent.connect(hyprEvent) + } + + function hyprEvent(e) { + titleProc.running = true + } +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Battery.qml b/quickshell/.config/quickshell/bar/blocks/Battery.qml new file mode 100644 index 0000000..10be0e0 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Battery.qml @@ -0,0 +1,26 @@ +import QtQuick +import Quickshell.Io +import "../" + +BarBlock { + property string battery + text: battery + + Process { + id: batteryProc + command: ["block_battery"] + running: true + + stdout: SplitParser { + onRead: data => battery = data + } + } + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: batteryProc.running = true + } +} + diff --git a/quickshell/.config/quickshell/bar/blocks/CPU.qml b/quickshell/.config/quickshell/bar/blocks/CPU.qml new file mode 100644 index 0000000..b7bca8d --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/CPU.qml @@ -0,0 +1,37 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import "../" + +Item { + implicitHeight: text.implicitHeight + implicitWidth: text.implicitWidth + + property string percentUsed + + Process { + id: cpuProc + command: ["sh", "-c", "top -bn 1 | grep \"%Cpu(s)\" | awk '{usage=100-$8; printf \"%02d%%\\n\", usage}'"] + running: true + + stdout: SplitParser { + onRead: data => percentUsed = data + } + } + + Timer { + interval: 2000 + running: true + repeat: true + onTriggered: cpuProc.running = true + } + + BarText { + id: text + text: "
%1
%2" + .arg(" ") // The rest of the string + .arg(percentUsed) // Symbol needs its own font + } +} diff --git a/quickshell/.config/quickshell/bar/blocks/Date.qml b/quickshell/.config/quickshell/bar/blocks/Date.qml new file mode 100644 index 0000000..8b94919 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Date.qml @@ -0,0 +1,8 @@ +import QtQuick +import "../" + +BarBlock { + id: text + text: ` ${Datetime.date}` +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Datetime.qml b/quickshell/.config/quickshell/bar/blocks/Datetime.qml new file mode 100644 index 0000000..743e785 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Datetime.qml @@ -0,0 +1,31 @@ +pragma Singleton + +import Quickshell +import Quickshell.Io +import QtQuick + +Singleton { + property string time; + property string date; + + Process { + id: dateProc + command: ["date", "+%a %e %b|%R"] + running: true + + stdout: SplitParser { + onRead: data => { + date = data.split("|")[0] + time = data.split("|")[1] + } + } + } + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: dateProc.running = true + } +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Icon.qml b/quickshell/.config/quickshell/bar/blocks/Icon.qml new file mode 100644 index 0000000..217e40c --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Icon.qml @@ -0,0 +1,11 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import "../" + +BarBlock { + Layout.preferredWidth: 30 + rightPadding: 5 + text: "" +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Memory.qml b/quickshell/.config/quickshell/bar/blocks/Memory.qml new file mode 100644 index 0000000..26900c0 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Memory.qml @@ -0,0 +1,29 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Io +import "../" + +BarBlock { + id: text + text: ` ${Math.floor(percentFree)}%` + + property real percentFree + + Process { + id: memProc + command: ["sh", "-c", "free | grep Mem | awk '{print $3/$2 * 100.0}'"] + running: true + + stdout: SplitParser { + onRead: data => percentFree = data + } + } + + Timer { + interval: 2000 + running: true + repeat: true + onTriggered: memProc.running = true + } +} diff --git a/quickshell/.config/quickshell/bar/blocks/Notifications.qml b/quickshell/.config/quickshell/bar/blocks/Notifications.qml new file mode 100644 index 0000000..3871cc4 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Notifications.qml @@ -0,0 +1,34 @@ +import QtQuick +import Quickshell.Services.Notifications +import "../" + +BarBlock { + id: root + property bool showNotification: false + + text: " " + notifServer.trackedNotifications.values.length + onClicked: function() { + showNotification = !showNotification + } + + NotificationServer { + id: notifServer + onNotification: (notification) => { + notification.tracked = true + } + } + + NotificationPanel { + text_color: root.color + visible: showNotification + + anchors { + top: parent.top + } + + margins { + top: 10 + } + } +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Sound.qml b/quickshell/.config/quickshell/bar/blocks/Sound.qml new file mode 100644 index 0000000..0553162 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Sound.qml @@ -0,0 +1,15 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.Pipewire +import "../" + +BarBlock { + id: text + text: ` ${Math.floor(sink?.audio.volume * 100)}%` + + property PwNode sink: Pipewire.defaultAudioSink + PwObjectTracker { objects: [ sink ] } +} + diff --git a/quickshell/.config/quickshell/bar/blocks/SystemTray.qml b/quickshell/.config/quickshell/bar/blocks/SystemTray.qml new file mode 100644 index 0000000..12818f1 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/SystemTray.qml @@ -0,0 +1,74 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.SystemTray +import "../" as Bar + +RowLayout { + spacing: 5 + + Repeater { + model: SystemTray.items + + MouseArea { + id: delegate + required property SystemTrayItem modelData + property alias item: delegate.modelData + + Layout.fillHeight: true + implicitWidth: icon.implicitWidth + 5 + + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + hoverEnabled: true + + onClicked: event => { + if (event.button == Qt.LeftButton) { + item.activate(); + } else if (event.button == Qt.MiddleButton) { + item.secondaryActivate(); + } else if (event.button == Qt.RightButton) { + menuAnchor.open(); + } + } + + onWheel: event => { + event.accepted = true; + const points = event.angleDelta.y / 120 + item.scroll(points, false); + } + + IconImage { + id: icon + anchors.centerIn: parent + source: item.icon + + implicitSize: 16 + } + + QsMenuAnchor { + id: menuAnchor + menu: item.menu + + anchor.window: delegate.QsWindow.window + anchor.adjustment: PopupAdjustment.Flip + + anchor.onAnchoring: { + const window = delegate.QsWindow.window; + const widgetRect = window.contentItem.mapFromItem(delegate, 0, delegate.height, delegate.width, delegate.height); + + menuAnchor.anchor.rect = widgetRect; + } + } + + Bar.Tooltip { + relativeItem: delegate.containsMouse ? delegate : null + + Label { + text: delegate.item.tooltipTitle || delegate.item.id + } + } + } + } +} diff --git a/quickshell/.config/quickshell/bar/blocks/Test.qml b/quickshell/.config/quickshell/bar/blocks/Test.qml new file mode 100644 index 0000000..14df09d --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Test.qml @@ -0,0 +1,77 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.SystemTray +import "../" + +Repeater { + // model: SystemTray.items + model: ScriptModel { + values: SystemTray.items.values + .filter((item) => item.id == "nm-tray") + } + + + BarBlock { + id: block + Layout.preferredWidth: 30 + leftPadding: 4 + text: " " + + required property SystemTrayItem modelData + property alias item: block.modelData + + MouseArea { + id: delegate + anchors.fill: block + + property alias item: block.item + + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + hoverEnabled: true + + onClicked: event => { + if (event.button == Qt.LeftButton) { + item.activate(); + } else if (event.button == Qt.MiddleButton) { + item.secondaryActivate(); + } else if (event.button == Qt.RightButton) { + menuAnchor.open(); + } + } + + onWheel: event => { + event.accepted = true; + const points = event.angleDelta.y / 120 + item.scroll(points, false); + } + + QsMenuAnchor { + id: menuAnchor + menu: item.menu + + anchor.window: delegate.QsWindow.window + anchor.adjustment: PopupAdjustment.Flip + + anchor.onAnchoring: { + console.log("here2") + const window = delegate.QsWindow.window; + const widgetRect = window.contentItem.mapFromItem(delegate, 0, delegate.height, delegate.width, delegate.height); + + menuAnchor.anchor.rect = widgetRect; + } + } + + Tooltip { + relativeItem: delegate.containsMouse ? delegate : null + + Label { + text: delegate.item.tooltipTitle || delegate.item.id + } + } + } + } +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Time.qml b/quickshell/.config/quickshell/bar/blocks/Time.qml new file mode 100644 index 0000000..770403d --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Time.qml @@ -0,0 +1,8 @@ +import QtQuick +import "../" + +BarBlock { + id: text + text: ` ${Datetime.time}` +} + diff --git a/quickshell/.config/quickshell/bar/blocks/Workspaces.qml b/quickshell/.config/quickshell/bar/blocks/Workspaces.qml new file mode 100644 index 0000000..5d32c48 --- /dev/null +++ b/quickshell/.config/quickshell/bar/blocks/Workspaces.qml @@ -0,0 +1,63 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import "root:/" +import "../" + +RowLayout { + spacing: 0 + property HyprlandMonitor monitor: Hyprland.monitorFor(screen) + + Repeater { + model: ScriptModel { + values: { + var seenEmpty = false + return [...Hyprland.workspaces.values] + .filter((ws) => { + if (ws.monitor !== monitor || ws.name.includes("special")) + return false + + // There is a flickering that can happen when switching from one + // empty workspace to another where both empty workspaces are shown + // on the bar at the same time. This ensures that only the first + // empty workspace is shown. + const isNumeric = /^\d+$/.test(ws.name); + if (!isNumeric) + return true; + if (!seenEmpty) { + seenEmpty = true + return true + } + return false; + }) + // Sort workspaces by id + .sort((a, b) => a.id - b.id) + } + } + + BarBlock { + property HyprlandWorkspace ws: modelData + property bool isActive: Hyprland.focusedMonitor?.activeWorkspace?.id === ws.id + property bool isOpen: monitor.activeWorkspace?.id === ws.id + property bool hasClients: ws.name.length > 2 + + dim: true + underline: isActive || isOpen + onClicked: function() { + Hyprland.dispatch(`workspace ${ws.id}`); + } + leftPadding: hasClients ? 2 : 0 + text: { + if (isActive) { + if (!hasClients) + return `${ws.name}` + var split_i = ws.id > 9 ? 3 : 2 + return `${ws.name.slice(0, split_i)}${ws.name.slice(split_i)}` + } + return ws.name + } + } + } +} + diff --git a/quickshell/.config/quickshell/shell.qml b/quickshell/.config/quickshell/shell.qml new file mode 100644 index 0000000..6d8d8da --- /dev/null +++ b/quickshell/.config/quickshell/shell.qml @@ -0,0 +1,8 @@ +//@ pragma UseQApplication +import Quickshell +import "bar" as Bar + +ShellRoot { + Bar.Bar {} +} +