From 22fa13cb191b8fc700fdfccfcc7ff149fdde4fd7 Mon Sep 17 00:00:00 2001 From: Itess Date: Sun, 27 Apr 2025 15:43:52 +0200 Subject: feat: add hyprpicker waybar module --- config/waybar/ModulesCustom | 7 +++++++ config/waybar/style/[0 VERTICAL] Golden Noir.css | 1 + config/waybar/style/[0 VERTICAL] Oglo Chicklets.css | 1 + config/waybar/style/[0 VERTICAL] [Catpuccin] Mocha.css | 1 + config/waybar/style/[Black & White] Monochrome.css | 1 + config/waybar/style/[Catppuccin] Frappe.css | 1 + config/waybar/style/[Catppuccin] Latte.css | 1 + config/waybar/style/[Catppuccin] Mocha.css | 1 + config/waybar/style/[Colored] Chroma Glow.css | 1 + config/waybar/style/[Colored] Translucent.css | 1 + config/waybar/style/[Colorful] Aurora Blossom.css | 1 + config/waybar/style/[Colorful] Aurora.css | 1 + config/waybar/style/[Colorful] Oglo Chicklets.css | 1 + config/waybar/style/[Colorful] Rainbow Spectrum.css | 1 + config/waybar/style/[Dark] Golden Eclipse.css | 1 + config/waybar/style/[Dark] Golden Noir.css | 1 + config/waybar/style/[Dark] Latte-Wallust combined v2.css | 1 + config/waybar/style/[Dark] Latte-Wallust combined.css | 1 + config/waybar/style/[Dark] Purpl.css | 1 + config/waybar/style/[Dark] Wallust Obsidian Edge.css | 1 + config/waybar/style/[Extra] Arrow.css | 1 + config/waybar/style/[Extra] Crimson.css | 1 + config/waybar/style/[Extra] EverForest.css | 1 + config/waybar/style/[Extra] ML4W starter.css | 1 + config/waybar/style/[Extra] Mauve.css | 1 + config/waybar/style/[Extra] Modern-Combined - Transparent.css | 1 + config/waybar/style/[Extra] Neon Circuit.css | 1 + config/waybar/style/[Extra] Prismatic Glow.css | 1 + config/waybar/style/[Extra] Rose Pine.css | 1 + config/waybar/style/[Extra] Simple Pink.css | 1 + config/waybar/style/[Light] Monochrome Contrast.css | 1 + config/waybar/style/[Light] Obsidian Glow.css | 1 + config/waybar/style/[Rainbow] RGB Bordered.css | 1 + config/waybar/style/[Retro] Simple Style.css | 1 + config/waybar/style/[Transparent] Crystal Clear.css | 1 + config/waybar/style/[VERTICAL] [Catpuccin] Mocha.css | 1 + config/waybar/style/[WALLUST] ML4W-modern-mixed.css | 1 + config/waybar/style/[WALLUST] ML4W-modern.css | 1 + config/waybar/style/[Wallust Bordered] Chroma Fusion Edge.css | 1 + config/waybar/style/[Wallust Bordered] Chroma Simple.css | 1 + config/waybar/style/[Wallust Transparent] Crystal Clear.css | 1 + config/waybar/style/[Wallust] Box type.css | 1 + config/waybar/style/[Wallust] Chroma Edge.css | 1 + config/waybar/style/[Wallust] Chroma Fusion.css | 1 + config/waybar/style/[Wallust] Chroma Tally V2.css | 1 + config/waybar/style/[Wallust] Chroma Tally.css | 1 + config/waybar/style/[Wallust] Colored.css | 1 + config/waybar/style/[Wallust] Simple.css | 1 + 48 files changed, 54 insertions(+) (limited to 'config') diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index bde9bbc2..c06cd7ac 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -17,6 +17,13 @@ "tooltip": true, }, +"custom/hyprpicker": { + "format": "", + "on-click": "hyprpicker | wl-copy", + "tooltip": true, + "tooltip-format": "Hyprpicker", +}, + "custom/file_manager": { "format": " ", "on-click": "xdg-open . &", diff --git a/config/waybar/style/[0 VERTICAL] Golden Noir.css b/config/waybar/style/[0 VERTICAL] Golden Noir.css index 8aa3ad74..c64fedf9 100644 --- a/config/waybar/style/[0 VERTICAL] Golden Noir.css +++ b/config/waybar/style/[0 VERTICAL] Golden Noir.css @@ -140,6 +140,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[0 VERTICAL] Oglo Chicklets.css b/config/waybar/style/[0 VERTICAL] Oglo Chicklets.css index 81ce148b..3a6084cb 100644 --- a/config/waybar/style/[0 VERTICAL] Oglo Chicklets.css +++ b/config/waybar/style/[0 VERTICAL] Oglo Chicklets.css @@ -102,6 +102,7 @@ button.active { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[0 VERTICAL] [Catpuccin] Mocha.css b/config/waybar/style/[0 VERTICAL] [Catpuccin] Mocha.css index 37b67a43..971dc40f 100644 --- a/config/waybar/style/[0 VERTICAL] [Catpuccin] Mocha.css +++ b/config/waybar/style/[0 VERTICAL] [Catpuccin] Mocha.css @@ -112,6 +112,7 @@ tooltip label { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Black & White] Monochrome.css b/config/waybar/style/[Black & White] Monochrome.css index 6106422a..700bebfc 100644 --- a/config/waybar/style/[Black & White] Monochrome.css +++ b/config/waybar/style/[Black & White] Monochrome.css @@ -149,6 +149,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Catppuccin] Frappe.css b/config/waybar/style/[Catppuccin] Frappe.css index c9a02d0f..82f79678 100644 --- a/config/waybar/style/[Catppuccin] Frappe.css +++ b/config/waybar/style/[Catppuccin] Frappe.css @@ -113,6 +113,7 @@ window#waybar.hidden { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Catppuccin] Latte.css b/config/waybar/style/[Catppuccin] Latte.css index b87c543f..80608e53 100644 --- a/config/waybar/style/[Catppuccin] Latte.css +++ b/config/waybar/style/[Catppuccin] Latte.css @@ -112,6 +112,7 @@ window#waybar.hidden { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Catppuccin] Mocha.css b/config/waybar/style/[Catppuccin] Mocha.css index 74892b69..67f4efa5 100644 --- a/config/waybar/style/[Catppuccin] Mocha.css +++ b/config/waybar/style/[Catppuccin] Mocha.css @@ -135,6 +135,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Colored] Chroma Glow.css b/config/waybar/style/[Colored] Chroma Glow.css index cc1b0de8..2497d69f 100644 --- a/config/waybar/style/[Colored] Chroma Glow.css +++ b/config/waybar/style/[Colored] Chroma Glow.css @@ -136,6 +136,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Colored] Translucent.css b/config/waybar/style/[Colored] Translucent.css index 0a69da90..50803ef7 100644 --- a/config/waybar/style/[Colored] Translucent.css +++ b/config/waybar/style/[Colored] Translucent.css @@ -137,6 +137,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Colorful] Aurora Blossom.css b/config/waybar/style/[Colorful] Aurora Blossom.css index 003dabbf..dbd405d2 100644 --- a/config/waybar/style/[Colorful] Aurora Blossom.css +++ b/config/waybar/style/[Colorful] Aurora Blossom.css @@ -128,6 +128,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Colorful] Aurora.css b/config/waybar/style/[Colorful] Aurora.css index debbc873..92c058c6 100644 --- a/config/waybar/style/[Colorful] Aurora.css +++ b/config/waybar/style/[Colorful] Aurora.css @@ -117,6 +117,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Colorful] Oglo Chicklets.css b/config/waybar/style/[Colorful] Oglo Chicklets.css index 98efcc2b..b5ac3584 100644 --- a/config/waybar/style/[Colorful] Oglo Chicklets.css +++ b/config/waybar/style/[Colorful] Oglo Chicklets.css @@ -102,6 +102,7 @@ button.active { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Colorful] Rainbow Spectrum.css b/config/waybar/style/[Colorful] Rainbow Spectrum.css index fd4e5cb5..6ca5906f 100644 --- a/config/waybar/style/[Colorful] Rainbow Spectrum.css +++ b/config/waybar/style/[Colorful] Rainbow Spectrum.css @@ -120,6 +120,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Dark] Golden Eclipse.css b/config/waybar/style/[Dark] Golden Eclipse.css index b2d1079f..af3160a6 100644 --- a/config/waybar/style/[Dark] Golden Eclipse.css +++ b/config/waybar/style/[Dark] Golden Eclipse.css @@ -75,6 +75,7 @@ window#waybar.hidden { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Dark] Golden Noir.css b/config/waybar/style/[Dark] Golden Noir.css index 9fe7b9c8..17025266 100644 --- a/config/waybar/style/[Dark] Golden Noir.css +++ b/config/waybar/style/[Dark] Golden Noir.css @@ -140,6 +140,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Dark] Latte-Wallust combined v2.css b/config/waybar/style/[Dark] Latte-Wallust combined v2.css index a937724c..46d55346 100644 --- a/config/waybar/style/[Dark] Latte-Wallust combined v2.css +++ b/config/waybar/style/[Dark] Latte-Wallust combined v2.css @@ -154,6 +154,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Dark] Latte-Wallust combined.css b/config/waybar/style/[Dark] Latte-Wallust combined.css index 51533e8e..ea5c08a5 100644 --- a/config/waybar/style/[Dark] Latte-Wallust combined.css +++ b/config/waybar/style/[Dark] Latte-Wallust combined.css @@ -159,6 +159,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Dark] Purpl.css b/config/waybar/style/[Dark] Purpl.css index c2ded5ff..5e2ff8aa 100644 --- a/config/waybar/style/[Dark] Purpl.css +++ b/config/waybar/style/[Dark] Purpl.css @@ -144,6 +144,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Dark] Wallust Obsidian Edge.css b/config/waybar/style/[Dark] Wallust Obsidian Edge.css index 22998299..94ad7cac 100644 --- a/config/waybar/style/[Dark] Wallust Obsidian Edge.css +++ b/config/waybar/style/[Dark] Wallust Obsidian Edge.css @@ -130,6 +130,7 @@ tooltip label { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Arrow.css b/config/waybar/style/[Extra] Arrow.css index 16aab612..9435d226 100644 --- a/config/waybar/style/[Extra] Arrow.css +++ b/config/waybar/style/[Extra] Arrow.css @@ -106,6 +106,7 @@ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Crimson.css b/config/waybar/style/[Extra] Crimson.css index a71f89e6..cf9e7db2 100644 --- a/config/waybar/style/[Extra] Crimson.css +++ b/config/waybar/style/[Extra] Crimson.css @@ -128,6 +128,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] EverForest.css b/config/waybar/style/[Extra] EverForest.css index ad2b51bc..f0014762 100644 --- a/config/waybar/style/[Extra] EverForest.css +++ b/config/waybar/style/[Extra] EverForest.css @@ -308,6 +308,7 @@ window#waybar.hidden { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] ML4W starter.css b/config/waybar/style/[Extra] ML4W starter.css index 79456a84..8d651da6 100644 --- a/config/waybar/style/[Extra] ML4W starter.css +++ b/config/waybar/style/[Extra] ML4W starter.css @@ -177,6 +177,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Mauve.css b/config/waybar/style/[Extra] Mauve.css index 816ed777..ec24bec7 100644 --- a/config/waybar/style/[Extra] Mauve.css +++ b/config/waybar/style/[Extra] Mauve.css @@ -147,6 +147,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Modern-Combined - Transparent.css b/config/waybar/style/[Extra] Modern-Combined - Transparent.css index f4184817..c4ff7197 100644 --- a/config/waybar/style/[Extra] Modern-Combined - Transparent.css +++ b/config/waybar/style/[Extra] Modern-Combined - Transparent.css @@ -161,6 +161,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Neon Circuit.css b/config/waybar/style/[Extra] Neon Circuit.css index 2415841a..7fbb39a5 100644 --- a/config/waybar/style/[Extra] Neon Circuit.css +++ b/config/waybar/style/[Extra] Neon Circuit.css @@ -125,6 +125,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Prismatic Glow.css b/config/waybar/style/[Extra] Prismatic Glow.css index cbcedfc6..8179352d 100644 --- a/config/waybar/style/[Extra] Prismatic Glow.css +++ b/config/waybar/style/[Extra] Prismatic Glow.css @@ -172,6 +172,7 @@ window#waybar.empty { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Rose Pine.css b/config/waybar/style/[Extra] Rose Pine.css index 1503355a..023b03a2 100644 --- a/config/waybar/style/[Extra] Rose Pine.css +++ b/config/waybar/style/[Extra] Rose Pine.css @@ -148,6 +148,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Extra] Simple Pink.css b/config/waybar/style/[Extra] Simple Pink.css index 84b2491f..db63a02f 100644 --- a/config/waybar/style/[Extra] Simple Pink.css +++ b/config/waybar/style/[Extra] Simple Pink.css @@ -140,6 +140,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Light] Monochrome Contrast.css b/config/waybar/style/[Light] Monochrome Contrast.css index 6e8a2e41..95b34fcc 100644 --- a/config/waybar/style/[Light] Monochrome Contrast.css +++ b/config/waybar/style/[Light] Monochrome Contrast.css @@ -129,6 +129,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Light] Obsidian Glow.css b/config/waybar/style/[Light] Obsidian Glow.css index da8bb502..692ce5d5 100644 --- a/config/waybar/style/[Light] Obsidian Glow.css +++ b/config/waybar/style/[Light] Obsidian Glow.css @@ -116,6 +116,7 @@ tooltip label { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Rainbow] RGB Bordered.css b/config/waybar/style/[Rainbow] RGB Bordered.css index e2df8c39..b38ae40d 100644 --- a/config/waybar/style/[Rainbow] RGB Bordered.css +++ b/config/waybar/style/[Rainbow] RGB Bordered.css @@ -136,6 +136,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Retro] Simple Style.css b/config/waybar/style/[Retro] Simple Style.css index fc50013c..8a601487 100644 --- a/config/waybar/style/[Retro] Simple Style.css +++ b/config/waybar/style/[Retro] Simple Style.css @@ -90,6 +90,7 @@ window#waybar { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Transparent] Crystal Clear.css b/config/waybar/style/[Transparent] Crystal Clear.css index b0cbaaf1..b355c176 100644 --- a/config/waybar/style/[Transparent] Crystal Clear.css +++ b/config/waybar/style/[Transparent] Crystal Clear.css @@ -111,6 +111,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[VERTICAL] [Catpuccin] Mocha.css b/config/waybar/style/[VERTICAL] [Catpuccin] Mocha.css index cd2d9165..d265506c 100644 --- a/config/waybar/style/[VERTICAL] [Catpuccin] Mocha.css +++ b/config/waybar/style/[VERTICAL] [Catpuccin] Mocha.css @@ -111,6 +111,7 @@ tooltip label { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[WALLUST] ML4W-modern-mixed.css b/config/waybar/style/[WALLUST] ML4W-modern-mixed.css index 5df79cb6..f1b64745 100644 --- a/config/waybar/style/[WALLUST] ML4W-modern-mixed.css +++ b/config/waybar/style/[WALLUST] ML4W-modern-mixed.css @@ -161,6 +161,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[WALLUST] ML4W-modern.css b/config/waybar/style/[WALLUST] ML4W-modern.css index b2a97488..048f972b 100644 --- a/config/waybar/style/[WALLUST] ML4W-modern.css +++ b/config/waybar/style/[WALLUST] ML4W-modern.css @@ -168,6 +168,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust Bordered] Chroma Fusion Edge.css b/config/waybar/style/[Wallust Bordered] Chroma Fusion Edge.css index e3224529..812dbf6f 100644 --- a/config/waybar/style/[Wallust Bordered] Chroma Fusion Edge.css +++ b/config/waybar/style/[Wallust Bordered] Chroma Fusion Edge.css @@ -100,6 +100,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust Bordered] Chroma Simple.css b/config/waybar/style/[Wallust Bordered] Chroma Simple.css index 63d66b26..3bf56789 100644 --- a/config/waybar/style/[Wallust Bordered] Chroma Simple.css +++ b/config/waybar/style/[Wallust Bordered] Chroma Simple.css @@ -125,6 +125,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust Transparent] Crystal Clear.css b/config/waybar/style/[Wallust Transparent] Crystal Clear.css index 54c7f2f3..cddee7e7 100644 --- a/config/waybar/style/[Wallust Transparent] Crystal Clear.css +++ b/config/waybar/style/[Wallust Transparent] Crystal Clear.css @@ -144,6 +144,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Box type.css b/config/waybar/style/[Wallust] Box type.css index 7c53ef92..84c85cf6 100644 --- a/config/waybar/style/[Wallust] Box type.css +++ b/config/waybar/style/[Wallust] Box type.css @@ -125,6 +125,7 @@ window#waybar.empty #window { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Chroma Edge.css b/config/waybar/style/[Wallust] Chroma Edge.css index 0cbaa6c0..3530c3af 100644 --- a/config/waybar/style/[Wallust] Chroma Edge.css +++ b/config/waybar/style/[Wallust] Chroma Edge.css @@ -128,6 +128,7 @@ tooltip label{ #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Chroma Fusion.css b/config/waybar/style/[Wallust] Chroma Fusion.css index 079c266a..d740c78f 100644 --- a/config/waybar/style/[Wallust] Chroma Fusion.css +++ b/config/waybar/style/[Wallust] Chroma Fusion.css @@ -101,6 +101,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Chroma Tally V2.css b/config/waybar/style/[Wallust] Chroma Tally V2.css index 8251d950..8082331b 100644 --- a/config/waybar/style/[Wallust] Chroma Tally V2.css +++ b/config/waybar/style/[Wallust] Chroma Tally V2.css @@ -113,6 +113,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Chroma Tally.css b/config/waybar/style/[Wallust] Chroma Tally.css index c9938eb3..a6fd1ee9 100644 --- a/config/waybar/style/[Wallust] Chroma Tally.css +++ b/config/waybar/style/[Wallust] Chroma Tally.css @@ -112,6 +112,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Colored.css b/config/waybar/style/[Wallust] Colored.css index 381417dd..435f3651 100644 --- a/config/waybar/style/[Wallust] Colored.css +++ b/config/waybar/style/[Wallust] Colored.css @@ -153,6 +153,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, diff --git a/config/waybar/style/[Wallust] Simple.css b/config/waybar/style/[Wallust] Simple.css index 0990a80c..c2206c10 100644 --- a/config/waybar/style/[Wallust] Simple.css +++ b/config/waybar/style/[Wallust] Simple.css @@ -132,6 +132,7 @@ tooltip { #custom-swaync, #custom-tty, #custom-updater, +#custom-hyprpicker, #custom-weather, #custom-weather.clearNight, #custom-weather.cloudyFoggyDay, -- cgit v1.2.3 From 6594a935a8c0c4b3bc90e6d5f5d74829c06c53f7 Mon Sep 17 00:00:00 2001 From: aditya-an1l Date: Thu, 22 May 2025 11:08:00 +0530 Subject: feat: Add Power Info (level and status) in Hyprlock Window - Display current power level (battery percentage) and power status (full, charging, discharging) at the bottom right section of Hyprlock - This feature allows laptop user to view the power info when their device are locked, especially if the device is getting discharged. --- config/hypr/hyprlock.conf | 12 ++++++++++++ config/hypr/scripts/Power.sh | 9 +++++++++ 2 files changed, 21 insertions(+) create mode 100644 config/hypr/scripts/Power.sh (limited to 'config') diff --git a/config/hypr/hyprlock.conf b/config/hypr/hyprlock.conf index a0d075cb..5222138a 100644 --- a/config/hypr/hyprlock.conf +++ b/config/hypr/hyprlock.conf @@ -156,6 +156,18 @@ label { valign = bottom } +# battery +label { + monitor = + text = cmd[update:1000] echo " "$($Scripts/Power.sh)" " + color = $color13 + font_size = 18 + font_family = Victor Mono Bold Oblique + position = 0, 30 + halign = right + valign = bottom +} + # weather edit the scripts for locations # weather scripts are located in ~/.config/hypr/UserScripts Weather.sh and/or Weather.py # see https://github.com/JaKooLit/Hyprland-Dots/wiki/TIPS#%EF%B8%8F-weather-app-related-for-waybar-and-hyprlock diff --git a/config/hypr/scripts/Power.sh b/config/hypr/scripts/Power.sh new file mode 100644 index 00000000..42431ea9 --- /dev/null +++ b/config/hypr/scripts/Power.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +for i in {0..3}; do + if [ -f /sys/class/power_supply/BAT$i/capacity ]; then + battery_level=$(cat /sys/class/power_supply/BAT$i/status) + battery_capacity=$(cat /sys/class/power_supply/BAT$i/capacity) + echo "Power: $battery_capacity% ($battery_level)" + fi +done -- cgit v1.2.3 From 4478adb2b0c65fab3c43621d1bf76f14fd6aaae2 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Sun, 25 May 2025 21:40:41 +0900 Subject: updated version for development --- config/hypr/v2.3.15 | 5 ----- config/hypr/v2.3.16 | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 config/hypr/v2.3.15 create mode 100644 config/hypr/v2.3.16 (limited to 'config') diff --git a/config/hypr/v2.3.15 b/config/hypr/v2.3.15 deleted file mode 100644 index 31b3414d..00000000 --- a/config/hypr/v2.3.15 +++ /dev/null @@ -1,5 +0,0 @@ -### https://github.com/JaKooLit ### -## https://github.com/JaKooLit/Hyprland-Dots -## This is to have a reference of which version would be - -## note that this will always be higher than the released versions \ No newline at end of file diff --git a/config/hypr/v2.3.16 b/config/hypr/v2.3.16 new file mode 100644 index 00000000..31b3414d --- /dev/null +++ b/config/hypr/v2.3.16 @@ -0,0 +1,5 @@ +### https://github.com/JaKooLit ### +## https://github.com/JaKooLit/Hyprland-Dots +## This is to have a reference of which version would be + +## note that this will always be higher than the released versions \ No newline at end of file -- cgit v1.2.3 From 2c33cef6fdee97ac9a13f0f4bf49158aa48c9f03 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sun, 25 May 2025 20:02:03 +0530 Subject: Improved Dropdown terminal --- config/hypr/UserConfigs/UserKeybinds.conf | 2 +- config/hypr/scripts/Dropterminal.sh | 65 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100755 config/hypr/scripts/Dropterminal.sh (limited to 'config') diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index 871f2ffb..ce501d85 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -39,7 +39,7 @@ bind = $mainMod SHIFT, F, fullscreen # whole full screen bind = $mainMod CTRL, F, fullscreen, 1 # fake full screen bind = $mainMod, SPACE, togglefloating, #Float Mode bind = $mainMod ALT, SPACE, exec, hyprctl dispatch workspaceopt allfloat #All Float Mode -bind = $mainMod SHIFT, Return, exec, [float; move 15% 5%; size 70% 60%] $term # Dropdown terminal +bind = $mainMod SHIFT, Return, exec, $scriptsDir/Dropterminal.sh $term # Dropdown terminal # Desktop zooming or magnifier bind = $mainMod ALT, mouse_down, exec, hyprctl keyword cursor:zoom_factor "$(hyprctl getoption cursor:zoom_factor | awk 'NR==1 {factor = $2; if (factor < 1) {factor = 1}; print factor * 2.0}')" diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh new file mode 100755 index 00000000..a160c3e9 --- /dev/null +++ b/config/hypr/scripts/Dropterminal.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ## +# Dropdown Terminal Usage: ./dropdown.sh [foot|kitty] + +TERMINAL="$1" +SPECIAL_WS="special:scratchpad" + +# Validate input and set CLASS and CMD +case "$TERMINAL" in + foot) + CLASS="foot-dropterminal" + TERMINAL_CMD="foot --app-id $CLASS" + ;; + kitty) + CLASS="kitty-dropterminal" + TERMINAL_CMD="kitty --class $CLASS" + ;; + *) + echo "Invalid or missing terminal argument. Usage: $0 [foot|kitty]" + exit 1 + ;; +esac + +# Get the current workspace +CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id') + +# Function to check if terminal exists +terminal_exists() { + hyprctl clients -j | jq -e --arg CLASS "$CLASS" 'any(.[]; .class == $CLASS)' >/dev/null 2>&1 +} + +# Function to check if terminal is in special workspace +terminal_in_special() { + hyprctl clients -j | jq -e --arg CLASS "$CLASS" 'any(.[]; .class == $CLASS and .workspace.name == "special:scratchpad")' >/dev/null 2>&1 +} + +# Function to get terminal address +get_terminal_address() { + hyprctl clients -j | jq -r --arg CLASS "$CLASS" '.[] | select(.class == $CLASS) | .address' +} + +if terminal_exists; then + TERMINAL_ADDR=$(get_terminal_address) + + if terminal_in_special; then + echo "Bringing terminal to workspace $CURRENT_WS and pinning" + hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$TERMINAL_ADDR" + hyprctl dispatch pin "address:$TERMINAL_ADDR" + hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" + else + echo "Unpinning and hiding terminal to special workspace" + hyprctl dispatch pin "address:$TERMINAL_ADDR" # Unpin (toggle) + sleep 0.1 + hyprctl dispatch movetoworkspacesilent "$SPECIAL_WS,address:$TERMINAL_ADDR" + fi +else + echo "Creating new dropdown terminal with command: $TERMINAL_CMD" + hyprctl dispatch exec "[float; move 25% 5%; size 50% 50%] $TERMINAL_CMD" + sleep 0.5 + if terminal_exists; then + TERMINAL_ADDR=$(get_terminal_address) + hyprctl dispatch pin "address:$TERMINAL_ADDR" + hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" + fi +fi -- cgit v1.2.3 From c6c06f0448413454b9ea58af7526c55c360b0e97 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 27 May 2025 03:58:55 +0900 Subject: updated Power.sh, renamed to Battery.sh since it is about battery information --- config/hypr/hyprlock-1080p.conf | 12 ++++++++++++ config/hypr/hyprlock.conf | 4 ++-- config/hypr/scripts/Battery.sh | 9 +++++++++ config/hypr/scripts/Power.sh | 9 --------- 4 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 config/hypr/scripts/Battery.sh delete mode 100644 config/hypr/scripts/Power.sh (limited to 'config') diff --git a/config/hypr/hyprlock-1080p.conf b/config/hypr/hyprlock-1080p.conf index a0e105e0..70c00c16 100644 --- a/config/hypr/hyprlock-1080p.conf +++ b/config/hypr/hyprlock-1080p.conf @@ -156,6 +156,18 @@ label { valign = bottom } +# battery information +label { + monitor = + text = cmd[update:1000] echo " "$($Scripts/Battery.sh)" " + color = $color13 + font_size = 16 + font_family = Victor Mono Bold Oblique + position = 0, 50 + halign = right + valign = bottom +} + # weather edit the scripts for locations # weather scripts are located in ~/.config/hypr/UserScripts Weather.sh and/or Weather.py # see https://github.com/JaKooLit/Hyprland-Dots/wiki/TIPS#%EF%B8%8F-weather-app-related-for-waybar-and-hyprlock diff --git a/config/hypr/hyprlock.conf b/config/hypr/hyprlock.conf index 80a94fb1..b67bba51 100644 --- a/config/hypr/hyprlock.conf +++ b/config/hypr/hyprlock.conf @@ -157,10 +157,10 @@ label { valign = bottom } -# battery +# battery information label { monitor = - text = cmd[update:1000] echo " "$($Scripts/Power.sh)" " + text = cmd[update:1000] echo " "$($Scripts/Battery.sh)" " color = $color13 font_size = 18 font_family = Victor Mono Bold Oblique diff --git a/config/hypr/scripts/Battery.sh b/config/hypr/scripts/Battery.sh new file mode 100644 index 00000000..d7830058 --- /dev/null +++ b/config/hypr/scripts/Battery.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +for i in {0..3}; do + if [ -f /sys/class/power_supply/BAT$i/capacity ]; then + battery_level=$(cat /sys/class/power_supply/BAT$i/status) + battery_capacity=$(cat /sys/class/power_supply/BAT$i/capacity) + echo "Battery: $battery_capacity% ($battery_level)" + fi +done diff --git a/config/hypr/scripts/Power.sh b/config/hypr/scripts/Power.sh deleted file mode 100644 index 42431ea9..00000000 --- a/config/hypr/scripts/Power.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -for i in {0..3}; do - if [ -f /sys/class/power_supply/BAT$i/capacity ]; then - battery_level=$(cat /sys/class/power_supply/BAT$i/status) - battery_capacity=$(cat /sys/class/power_supply/BAT$i/capacity) - echo "Power: $battery_capacity% ($battery_level)" - fi -done -- cgit v1.2.3 From 78a46392e550170b47fc62cc189fcd0d5ac7e5c2 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 27 May 2025 04:03:56 +0900 Subject: updated hyprlock 1080p --- config/hypr/hyprlock-1080p.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/hypr/hyprlock-1080p.conf b/config/hypr/hyprlock-1080p.conf index 70c00c16..14f2f35e 100644 --- a/config/hypr/hyprlock-1080p.conf +++ b/config/hypr/hyprlock-1080p.conf @@ -163,7 +163,7 @@ label { color = $color13 font_size = 16 font_family = Victor Mono Bold Oblique - position = 0, 50 + position = 0, 30 halign = right valign = bottom } -- cgit v1.2.3 From 84466963633538013e84c74c2d8b2ce33e06c504 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Tue, 27 May 2025 10:11:45 +0530 Subject: Updated dropdown terminal script to use addressed based window tracking instead of class --- config/hypr/scripts/Dropterminal.sh | 140 ++++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 31 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index a160c3e9..98420a24 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -1,65 +1,143 @@ #!/bin/bash # /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ## -# Dropdown Terminal Usage: ./dropdown.sh [foot|kitty] +# Dropdown Terminal +# Usage: ./dropdown.sh [-d] +# Example: ./dropdown.sh foot +# ./dropdown.sh -d foot (with debug output) +# ./dropdown.sh "kitty -e zsh" +# ./dropdown.sh "alacritty --working-directory /home/user" -TERMINAL="$1" +DEBUG=false SPECIAL_WS="special:scratchpad" +ADDR_FILE="/tmp/dropdown_terminal_addr" -# Validate input and set CLASS and CMD -case "$TERMINAL" in - foot) - CLASS="foot-dropterminal" - TERMINAL_CMD="foot --app-id $CLASS" - ;; - kitty) - CLASS="kitty-dropterminal" - TERMINAL_CMD="kitty --class $CLASS" - ;; - *) - echo "Invalid or missing terminal argument. Usage: $0 [foot|kitty]" - exit 1 - ;; -esac +# Parse arguments +if [ "$1" = "-d" ]; then + DEBUG=true + shift +fi + +TERMINAL_CMD="$1" + +# Debug echo function +debug_echo() { + if [ "$DEBUG" = true ]; then + echo "$@" + fi +} + +# Validate input +if [ -z "$TERMINAL_CMD" ]; then + echo "Missing terminal command. Usage: $0 [-d] " + echo "Examples:" + echo " $0 foot" + echo " $0 -d foot (with debug output)" + echo " $0 'kitty -e zsh'" + echo " $0 'alacritty --working-directory /home/user'" + exit 1 +fi # Get the current workspace CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id') +# Function to get stored terminal address +get_terminal_address() { + if [ -f "$ADDR_FILE" ] && [ -s "$ADDR_FILE" ]; then + cat "$ADDR_FILE" + fi +} + # Function to check if terminal exists terminal_exists() { - hyprctl clients -j | jq -e --arg CLASS "$CLASS" 'any(.[]; .class == $CLASS)' >/dev/null 2>&1 + local addr=$(get_terminal_address) + if [ -n "$addr" ]; then + hyprctl clients -j | jq -e --arg ADDR "$addr" 'any(.[]; .address == $ADDR)' >/dev/null 2>&1 + else + return 1 + fi } # Function to check if terminal is in special workspace terminal_in_special() { - hyprctl clients -j | jq -e --arg CLASS "$CLASS" 'any(.[]; .class == $CLASS and .workspace.name == "special:scratchpad")' >/dev/null 2>&1 + local addr=$(get_terminal_address) + if [ -n "$addr" ]; then + hyprctl clients -j | jq -e --arg ADDR "$addr" 'any(.[]; .address == $ADDR and .workspace.name == "special:scratchpad")' >/dev/null 2>&1 + else + return 1 + fi } -# Function to get terminal address -get_terminal_address() { - hyprctl clients -j | jq -r --arg CLASS "$CLASS" '.[] | select(.class == $CLASS) | .address' +# Function to spawn terminal and capture its address +spawn_terminal() { + debug_echo "Creating new dropdown terminal with command: $TERMINAL_CMD" + + # Get window count and list before spawning + local windows_before=$(hyprctl clients -j) + local count_before=$(echo "$windows_before" | jq 'length') + + # Launch terminal with rules applied from the start + hyprctl dispatch exec "[float; move 25% 5%; size 50% 50%; pin] $TERMINAL_CMD" + + # Wait for window to appear + sleep 1 + + # Get windows after spawning + local windows_after=$(hyprctl clients -j) + local count_after=$(echo "$windows_after" | jq 'length') + + if [ "$count_after" -gt "$count_before" ]; then + # Find the new window by comparing before/after lists + local new_addr=$(comm -13 \ + <(echo "$windows_before" | jq -r '.[].address' | sort) \ + <(echo "$windows_after" | jq -r '.[].address' | sort) \ + | head -1) + + if [ -n "$new_addr" ] && [ "$new_addr" != "null" ]; then + # Store the address + echo "$new_addr" > "$ADDR_FILE" + debug_echo "Terminal created with address: $new_addr" + return 0 + fi + fi + + # Fallback: try to find by the most recently mapped window + local new_addr=$(hyprctl clients -j | jq -r 'sort_by(.focusHistoryID) | .[-1] | .address') + + if [ -n "$new_addr" ] && [ "$new_addr" != "null" ]; then + echo "$new_addr" > "$ADDR_FILE" + debug_echo "Terminal created with address: $new_addr (fallback method)" + return 0 + fi + + debug_echo "Failed to get terminal address" + return 1 } +# Main logic if terminal_exists; then TERMINAL_ADDR=$(get_terminal_address) + debug_echo "Found existing terminal: $TERMINAL_ADDR" if terminal_in_special; then - echo "Bringing terminal to workspace $CURRENT_WS and pinning" + debug_echo "Bringing terminal to workspace $CURRENT_WS and pinning" hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$TERMINAL_ADDR" hyprctl dispatch pin "address:$TERMINAL_ADDR" hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" else - echo "Unpinning and hiding terminal to special workspace" + debug_echo "Unpinning and hiding terminal to special workspace" hyprctl dispatch pin "address:$TERMINAL_ADDR" # Unpin (toggle) sleep 0.1 hyprctl dispatch movetoworkspacesilent "$SPECIAL_WS,address:$TERMINAL_ADDR" fi else - echo "Creating new dropdown terminal with command: $TERMINAL_CMD" - hyprctl dispatch exec "[float; move 25% 5%; size 50% 50%] $TERMINAL_CMD" - sleep 0.5 - if terminal_exists; then + debug_echo "No existing terminal found, creating new one" + if spawn_terminal; then TERMINAL_ADDR=$(get_terminal_address) - hyprctl dispatch pin "address:$TERMINAL_ADDR" - hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" + if [ -n "$TERMINAL_ADDR" ]; then + hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" + fi + else + echo "Failed to create terminal" + exit 1 fi -fi +fi \ No newline at end of file -- cgit v1.2.3 From c0943736e57b52d883c45d43d21adfb3067099b0 Mon Sep 17 00:00:00 2001 From: Humblemonk Date: Tue, 27 May 2025 00:48:38 -0400 Subject: Remove trailing whitespace from WindowRules.conf Noticed a trailing whitespace in the latest WindowRules.conf. --- config/hypr/UserConfigs/WindowRules.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'config') diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index 661b3a17..099cd2c8 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -44,7 +44,7 @@ windowrule = tag +screenshare, class:^(com.obsproject.Studio)$ windowrule = tag +im, class:^([Dd]iscord|[Ww]ebCord|[Vv]esktop)$ windowrule = tag +im, class:^([Ff]erdium)$ windowrule = tag +im, class:^([Ww]hatsapp-for-linux)$ -windowrule = tag +im, class:^(ZapZap|com.rtosta.zapzap)$ +windowrule = tag +im, class:^(ZapZap|com.rtosta.zapzap)$ windowrule = tag +im, class:^(org.telegram.desktop|io.github.tdesktop_x64.TDesktop)$ windowrule = tag +im, class:^(teams-for-linux)$ @@ -219,4 +219,4 @@ layerrule = ignorezero, notifications #layerrule = ignorezero, #layerrule = ignorezero, overview -#layerrule = blur, overview \ No newline at end of file +#layerrule = blur, overview -- cgit v1.2.3 From b56edcff4d48d215ad26e1c64729a8ea7c311f22 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Tue, 27 May 2025 21:45:24 +0530 Subject: Added animation similar to pyprland --- config/hypr/scripts/Dropterminal.sh | 196 ++++++++++++++++++++++++++++++------ 1 file changed, 168 insertions(+), 28 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index 98420a24..51cdf31f 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -1,16 +1,21 @@ #!/bin/bash # /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ## # Dropdown Terminal -# Usage: ./dropdown.sh [-d] -# Example: ./dropdown.sh foot -# ./dropdown.sh -d foot (with debug output) -# ./dropdown.sh "kitty -e zsh" -# ./dropdown.sh "alacritty --working-directory /home/user" +# Usage: ./Dropdown.sh [-d] +# Example: ./Dropdown.sh foot +# ./Dropdown.sh -d foot (with debug output) +# ./Dropdown.sh "kitty -e zsh" +# ./Dropdown.sh "alacritty --working-directory /home/user" DEBUG=false SPECIAL_WS="special:scratchpad" ADDR_FILE="/tmp/dropdown_terminal_addr" +# Animation settings +ANIMATION_DURATION=100 # milliseconds +SLIDE_STEPS=10 +SLIDE_DELAY=5 # milliseconds between steps + # Parse arguments if [ "$1" = "-d" ]; then DEBUG=true @@ -37,6 +42,91 @@ if [ -z "$TERMINAL_CMD" ]; then exit 1 fi +# Function to get window geometry +get_window_geometry() { + local addr="$1" + hyprctl clients -j | jq -r --arg ADDR "$addr" '.[] | select(.address == $ADDR) | "\(.at[0]) \(.at[1]) \(.size[0]) \(.size[1])"' +} + +# Function to animate window slide down (show) +animate_slide_down() { + local addr="$1" + local target_x="$2" + local target_y="$3" + local width="$4" + local height="$5" + + debug_echo "Animating slide down for window $addr to position $target_x,$target_y" + + # Start position (above screen) + local start_y=$((target_y - height - 50)) + + # Calculate step size + local step_y=$(((target_y - start_y) / SLIDE_STEPS)) + + # Move window to start position instantly (off-screen) + hyprctl dispatch movewindowpixel "exact $target_x $start_y,address:$addr" >/dev/null 2>&1 + sleep 0.05 + + # Animate slide down + for i in $(seq 1 $SLIDE_STEPS); do + local current_y=$((start_y + (step_y * i))) + hyprctl dispatch movewindowpixel "exact $target_x $current_y,address:$addr" >/dev/null 2>&1 + sleep 0.03 + done + + # Ensure final position is exact + hyprctl dispatch movewindowpixel "exact $target_x $target_y,address:$addr" >/dev/null 2>&1 +} + +# Function to animate window slide up (hide) +animate_slide_up() { + local addr="$1" + local start_x="$2" + local start_y="$3" + local width="$4" + local height="$5" + + debug_echo "Animating slide up for window $addr from position $start_x,$start_y" + + # End position (above screen) + local end_y=$((start_y - height - 50)) + + # Calculate step size + local step_y=$(((start_y - end_y) / SLIDE_STEPS)) + + # Animate slide up + for i in $(seq 1 $SLIDE_STEPS); do + local current_y=$((start_y - (step_y * i))) + hyprctl dispatch movewindowpixel "exact $start_x $current_y,address:$addr" >/dev/null 2>&1 + sleep 0.03 + done + + debug_echo "Slide up animation completed" +} + +# Function to get monitor info for centering +get_monitor_info() { + hyprctl monitors -j | jq -r '.[0] | "\(.x) \(.y) \(.width) \(.height)"' +} + +# Function to calculate dropdown position +calculate_dropdown_position() { + local monitor_info=$(get_monitor_info) + local mon_x=$(echo $monitor_info | cut -d' ' -f1) + local mon_y=$(echo $monitor_info | cut -d' ' -f2) + local mon_width=$(echo $monitor_info | cut -d' ' -f3) + local mon_height=$(echo $monitor_info | cut -d' ' -f4) + + # Calculate 50% width, 50% height, centered horizontally, 5% from top + local width=$((mon_width / 2)) + local height=$((mon_height / 2)) + local x=$((mon_x + (mon_width - width) / 2)) + local y=$((mon_y + mon_height / 20)) # 5% from top + + echo "$x $y $width $height" +} + # Get the current workspace CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id') @@ -71,41 +161,57 @@ terminal_in_special() { spawn_terminal() { debug_echo "Creating new dropdown terminal with command: $TERMINAL_CMD" - # Get window count and list before spawning - local windows_before=$(hyprctl clients -j) - local count_before=$(echo "$windows_before" | jq 'length') + # Calculate dropdown position for later use + pos_info=$(calculate_dropdown_position) + target_x=$(echo $pos_info | cut -d' ' -f1) + target_y=$(echo $pos_info | cut -d' ' -f2) + width=$(echo $pos_info | cut -d' ' -f3) + height=$(echo $pos_info | cut -d' ' -f4) + + debug_echo "Target position: ${target_x}x${target_y}, size: ${width}x${height}" - # Launch terminal with rules applied from the start - hyprctl dispatch exec "[float; move 25% 5%; size 50% 50%; pin] $TERMINAL_CMD" + # Get window count before spawning + windows_before=$(hyprctl clients -j) + count_before=$(echo "$windows_before" | jq 'length') + + # Launch terminal directly in special workspace to avoid visible spawn + hyprctl dispatch exec "[float; size $width $height; workspace special:scratchpad silent] $TERMINAL_CMD" # Wait for window to appear - sleep 1 + sleep 0.1 # Get windows after spawning - local windows_after=$(hyprctl clients -j) - local count_after=$(echo "$windows_after" | jq 'length') + windows_after=$(hyprctl clients -j) + count_after=$(echo "$windows_after" | jq 'length') + + new_addr="" if [ "$count_after" -gt "$count_before" ]; then # Find the new window by comparing before/after lists - local new_addr=$(comm -13 \ + new_addr=$(comm -13 \ <(echo "$windows_before" | jq -r '.[].address' | sort) \ <(echo "$windows_after" | jq -r '.[].address' | sort) \ | head -1) - - if [ -n "$new_addr" ] && [ "$new_addr" != "null" ]; then - # Store the address - echo "$new_addr" > "$ADDR_FILE" - debug_echo "Terminal created with address: $new_addr" - return 0 - fi fi # Fallback: try to find by the most recently mapped window - local new_addr=$(hyprctl clients -j | jq -r 'sort_by(.focusHistoryID) | .[-1] | .address') + if [ -z "$new_addr" ] || [ "$new_addr" = "null" ]; then + new_addr=$(hyprctl clients -j | jq -r 'sort_by(.focusHistoryID) | .[-1] | .address') + fi if [ -n "$new_addr" ] && [ "$new_addr" != "null" ]; then + # Store the address echo "$new_addr" > "$ADDR_FILE" - debug_echo "Terminal created with address: $new_addr (fallback method)" + debug_echo "Terminal created with address: $new_addr in special workspace" + + # Small delay to ensure it's properly in special workspace + sleep 0.2 + + # Now bring it back with the same animation as subsequent shows + hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$new_addr" + hyprctl dispatch pin "address:$new_addr" + animate_slide_down "$new_addr" "$target_x" "$target_y" "$width" "$height" + return 0 fi @@ -119,15 +225,49 @@ if terminal_exists; then debug_echo "Found existing terminal: $TERMINAL_ADDR" if terminal_in_special; then - debug_echo "Bringing terminal to workspace $CURRENT_WS and pinning" + debug_echo "Bringing terminal from scratchpad with slide down animation" + + # Calculate target position + pos_info=$(calculate_dropdown_position) + target_x=$(echo $pos_info | cut -d' ' -f1) + target_y=$(echo $pos_info | cut -d' ' -f2) + width=$(echo $pos_info | cut -d' ' -f3) + height=$(echo $pos_info | cut -d' ' -f4) + + # Move to current workspace and pin hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$TERMINAL_ADDR" hyprctl dispatch pin "address:$TERMINAL_ADDR" + + # Set size and animate slide down + hyprctl dispatch resizewindowpixel "exact $width $height,address:$TERMINAL_ADDR" + animate_slide_down "$TERMINAL_ADDR" "$target_x" "$target_y" "$width" "$height" + hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" else - debug_echo "Unpinning and hiding terminal to special workspace" - hyprctl dispatch pin "address:$TERMINAL_ADDR" # Unpin (toggle) - sleep 0.1 - hyprctl dispatch movetoworkspacesilent "$SPECIAL_WS,address:$TERMINAL_ADDR" + debug_echo "Hiding terminal to scratchpad with slide up animation" + + # Get current geometry for animation + geometry=$(get_window_geometry "$TERMINAL_ADDR") + if [ -n "$geometry" ]; then + curr_x=$(echo $geometry | cut -d' ' -f1) + curr_y=$(echo $geometry | cut -d' ' -f2) + curr_width=$(echo $geometry | cut -d' ' -f3) + curr_height=$(echo $geometry | cut -d' ' -f4) + + debug_echo "Current geometry: ${curr_x},${curr_y} ${curr_width}x${curr_height}" + + # Animate slide up first + animate_slide_up "$TERMINAL_ADDR" "$curr_x" "$curr_y" "$curr_width" "$curr_height" + + # Small delay then move to special workspace and unpin + sleep 0.1 + hyprctl dispatch pin "address:$TERMINAL_ADDR" # Unpin (toggle) + hyprctl dispatch movetoworkspacesilent "$SPECIAL_WS,address:$TERMINAL_ADDR" + else + debug_echo "Could not get window geometry, moving to scratchpad without animation" + hyprctl dispatch pin "address:$TERMINAL_ADDR" + hyprctl dispatch movetoworkspacesilent "$SPECIAL_WS,address:$TERMINAL_ADDR" + fi fi else debug_echo "No existing terminal found, creating new one" -- cgit v1.2.3 From d8aee89ba261d17597deff3fe00a7907adf240e2 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sat, 31 May 2025 08:18:33 +0530 Subject: Dropdown terminal improvements - Round 2 --- config/hypr/scripts/Dropterminal.sh | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index 51cdf31f..a8e1cda3 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -11,9 +11,15 @@ DEBUG=false SPECIAL_WS="special:scratchpad" ADDR_FILE="/tmp/dropdown_terminal_addr" +# Dropdown size and position configuration (percentages) +WIDTH_PERCENT=50 # Width as percentage of screen width +HEIGHT_PERCENT=50 # Height as percentage of screen height +X_PERCENT=25 # X position as percentage from left (25% centers a 50% width window) +Y_PERCENT=5 # Y position as percentage from top + # Animation settings ANIMATION_DURATION=100 # milliseconds -SLIDE_STEPS=10 +SLIDE_STEPS=5 SLIDE_DELAY=5 # milliseconds between steps # Parse arguments @@ -39,6 +45,12 @@ if [ -z "$TERMINAL_CMD" ]; then echo " $0 -d foot (with debug output)" echo " $0 'kitty -e zsh'" echo " $0 'alacritty --working-directory /home/user'" + echo "" + echo "Edit the script to modify size and position:" + echo " WIDTH_PERCENT - Width as percentage of screen (default: 50)" + echo " HEIGHT_PERCENT - Height as percentage of screen (default: 50)" + echo " X_PERCENT - X position from left as percentage (default: 25)" + echo " Y_PERCENT - Y position from top as percentage (default: 5)" exit 1 fi @@ -118,11 +130,11 @@ calculate_dropdown_position() { local mon_width=$(echo $monitor_info | cut -d' ' -f3) local mon_height=$(echo $monitor_info | cut -d' ' -f4) - # Calculate 50% width, 50% height, centered horizontally, 5% from top - local width=$((mon_width / 2)) - local height=$((mon_height / 2)) - local x=$((mon_x + (mon_width - width) / 2)) - local y=$((mon_y + mon_height / 20)) # 5% from top + # Calculate position and size based on percentages + local width=$((mon_width * WIDTH_PERCENT / 100)) + local height=$((mon_height * HEIGHT_PERCENT / 100)) + local x=$((mon_x + (mon_width * X_PERCENT / 100))) + local y=$((mon_y + (mon_height * Y_PERCENT / 100))) echo "$x $y $width $height" } @@ -175,7 +187,7 @@ spawn_terminal() { count_before=$(echo "$windows_before" | jq 'length') # Launch terminal directly in special workspace to avoid visible spawn - hyprctl dispatch exec "[float; size $width $height; workspace special:scratchpad silent] $TERMINAL_CMD" + hyprctl dispatch exec "[float; size $width $height; stayfocused; workspace special:scratchpad silent] $TERMINAL_CMD" # Wait for window to appear sleep 0.1 @@ -208,7 +220,8 @@ spawn_terminal() { sleep 0.2 # Now bring it back with the same animation as subsequent shows - hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$new_addr" + # Use movetoworkspacesilent to avoid affecting workspace history + hyprctl dispatch movetoworkspacesilent "$CURRENT_WS,address:$new_addr" hyprctl dispatch pin "address:$new_addr" animate_slide_down "$new_addr" "$target_x" "$target_y" "$width" "$height" @@ -234,8 +247,8 @@ if terminal_exists; then width=$(echo $pos_info | cut -d' ' -f3) height=$(echo $pos_info | cut -d' ' -f4) - # Move to current workspace and pin - hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$TERMINAL_ADDR" + # Use movetoworkspacesilent to avoid affecting workspace history + hyprctl dispatch movetoworkspacesilent "$CURRENT_WS,address:$TERMINAL_ADDR" hyprctl dispatch pin "address:$TERMINAL_ADDR" # Set size and animate slide down @@ -276,8 +289,5 @@ else if [ -n "$TERMINAL_ADDR" ]; then hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" fi - else - echo "Failed to create terminal" - exit 1 fi fi \ No newline at end of file -- cgit v1.2.3 From 713f9498329cd15457abe3d65b07ae9481510a22 Mon Sep 17 00:00:00 2001 From: "Ja.KooLit" <85185940+JaKooLit@users.noreply.github.com> Date: Sat, 31 May 2025 20:28:08 +0000 Subject: Revert "Dropdown terminal improvements - Round 2" --- config/hypr/scripts/Dropterminal.sh | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index a8e1cda3..51cdf31f 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -11,15 +11,9 @@ DEBUG=false SPECIAL_WS="special:scratchpad" ADDR_FILE="/tmp/dropdown_terminal_addr" -# Dropdown size and position configuration (percentages) -WIDTH_PERCENT=50 # Width as percentage of screen width -HEIGHT_PERCENT=50 # Height as percentage of screen height -X_PERCENT=25 # X position as percentage from left (25% centers a 50% width window) -Y_PERCENT=5 # Y position as percentage from top - # Animation settings ANIMATION_DURATION=100 # milliseconds -SLIDE_STEPS=5 +SLIDE_STEPS=10 SLIDE_DELAY=5 # milliseconds between steps # Parse arguments @@ -45,12 +39,6 @@ if [ -z "$TERMINAL_CMD" ]; then echo " $0 -d foot (with debug output)" echo " $0 'kitty -e zsh'" echo " $0 'alacritty --working-directory /home/user'" - echo "" - echo "Edit the script to modify size and position:" - echo " WIDTH_PERCENT - Width as percentage of screen (default: 50)" - echo " HEIGHT_PERCENT - Height as percentage of screen (default: 50)" - echo " X_PERCENT - X position from left as percentage (default: 25)" - echo " Y_PERCENT - Y position from top as percentage (default: 5)" exit 1 fi @@ -130,11 +118,11 @@ calculate_dropdown_position() { local mon_width=$(echo $monitor_info | cut -d' ' -f3) local mon_height=$(echo $monitor_info | cut -d' ' -f4) - # Calculate position and size based on percentages - local width=$((mon_width * WIDTH_PERCENT / 100)) - local height=$((mon_height * HEIGHT_PERCENT / 100)) - local x=$((mon_x + (mon_width * X_PERCENT / 100))) - local y=$((mon_y + (mon_height * Y_PERCENT / 100))) + # Calculate 50% width, 50% height, centered horizontally, 5% from top + local width=$((mon_width / 2)) + local height=$((mon_height / 2)) + local x=$((mon_x + (mon_width - width) / 2)) + local y=$((mon_y + mon_height / 20)) # 5% from top echo "$x $y $width $height" } @@ -187,7 +175,7 @@ spawn_terminal() { count_before=$(echo "$windows_before" | jq 'length') # Launch terminal directly in special workspace to avoid visible spawn - hyprctl dispatch exec "[float; size $width $height; stayfocused; workspace special:scratchpad silent] $TERMINAL_CMD" + hyprctl dispatch exec "[float; size $width $height; workspace special:scratchpad silent] $TERMINAL_CMD" # Wait for window to appear sleep 0.1 @@ -220,8 +208,7 @@ spawn_terminal() { sleep 0.2 # Now bring it back with the same animation as subsequent shows - # Use movetoworkspacesilent to avoid affecting workspace history - hyprctl dispatch movetoworkspacesilent "$CURRENT_WS,address:$new_addr" + hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$new_addr" hyprctl dispatch pin "address:$new_addr" animate_slide_down "$new_addr" "$target_x" "$target_y" "$width" "$height" @@ -247,8 +234,8 @@ if terminal_exists; then width=$(echo $pos_info | cut -d' ' -f3) height=$(echo $pos_info | cut -d' ' -f4) - # Use movetoworkspacesilent to avoid affecting workspace history - hyprctl dispatch movetoworkspacesilent "$CURRENT_WS,address:$TERMINAL_ADDR" + # Move to current workspace and pin + hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$TERMINAL_ADDR" hyprctl dispatch pin "address:$TERMINAL_ADDR" # Set size and animate slide down @@ -289,5 +276,8 @@ else if [ -n "$TERMINAL_ADDR" ]; then hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" fi + else + echo "Failed to create terminal" + exit 1 fi fi \ No newline at end of file -- cgit v1.2.3 From 4564b38d1b84888f153802cc5b4e9c32de4aa8a3 Mon Sep 17 00:00:00 2001 From: "Ja.KooLit" <85185940+JaKooLit@users.noreply.github.com> Date: Sat, 31 May 2025 20:37:06 +0000 Subject: Revert "Revert "Dropdown terminal improvements - Round 2"" --- config/hypr/scripts/Dropterminal.sh | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index 51cdf31f..a8e1cda3 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -11,9 +11,15 @@ DEBUG=false SPECIAL_WS="special:scratchpad" ADDR_FILE="/tmp/dropdown_terminal_addr" +# Dropdown size and position configuration (percentages) +WIDTH_PERCENT=50 # Width as percentage of screen width +HEIGHT_PERCENT=50 # Height as percentage of screen height +X_PERCENT=25 # X position as percentage from left (25% centers a 50% width window) +Y_PERCENT=5 # Y position as percentage from top + # Animation settings ANIMATION_DURATION=100 # milliseconds -SLIDE_STEPS=10 +SLIDE_STEPS=5 SLIDE_DELAY=5 # milliseconds between steps # Parse arguments @@ -39,6 +45,12 @@ if [ -z "$TERMINAL_CMD" ]; then echo " $0 -d foot (with debug output)" echo " $0 'kitty -e zsh'" echo " $0 'alacritty --working-directory /home/user'" + echo "" + echo "Edit the script to modify size and position:" + echo " WIDTH_PERCENT - Width as percentage of screen (default: 50)" + echo " HEIGHT_PERCENT - Height as percentage of screen (default: 50)" + echo " X_PERCENT - X position from left as percentage (default: 25)" + echo " Y_PERCENT - Y position from top as percentage (default: 5)" exit 1 fi @@ -118,11 +130,11 @@ calculate_dropdown_position() { local mon_width=$(echo $monitor_info | cut -d' ' -f3) local mon_height=$(echo $monitor_info | cut -d' ' -f4) - # Calculate 50% width, 50% height, centered horizontally, 5% from top - local width=$((mon_width / 2)) - local height=$((mon_height / 2)) - local x=$((mon_x + (mon_width - width) / 2)) - local y=$((mon_y + mon_height / 20)) # 5% from top + # Calculate position and size based on percentages + local width=$((mon_width * WIDTH_PERCENT / 100)) + local height=$((mon_height * HEIGHT_PERCENT / 100)) + local x=$((mon_x + (mon_width * X_PERCENT / 100))) + local y=$((mon_y + (mon_height * Y_PERCENT / 100))) echo "$x $y $width $height" } @@ -175,7 +187,7 @@ spawn_terminal() { count_before=$(echo "$windows_before" | jq 'length') # Launch terminal directly in special workspace to avoid visible spawn - hyprctl dispatch exec "[float; size $width $height; workspace special:scratchpad silent] $TERMINAL_CMD" + hyprctl dispatch exec "[float; size $width $height; stayfocused; workspace special:scratchpad silent] $TERMINAL_CMD" # Wait for window to appear sleep 0.1 @@ -208,7 +220,8 @@ spawn_terminal() { sleep 0.2 # Now bring it back with the same animation as subsequent shows - hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$new_addr" + # Use movetoworkspacesilent to avoid affecting workspace history + hyprctl dispatch movetoworkspacesilent "$CURRENT_WS,address:$new_addr" hyprctl dispatch pin "address:$new_addr" animate_slide_down "$new_addr" "$target_x" "$target_y" "$width" "$height" @@ -234,8 +247,8 @@ if terminal_exists; then width=$(echo $pos_info | cut -d' ' -f3) height=$(echo $pos_info | cut -d' ' -f4) - # Move to current workspace and pin - hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$TERMINAL_ADDR" + # Use movetoworkspacesilent to avoid affecting workspace history + hyprctl dispatch movetoworkspacesilent "$CURRENT_WS,address:$TERMINAL_ADDR" hyprctl dispatch pin "address:$TERMINAL_ADDR" # Set size and animate slide down @@ -276,8 +289,5 @@ else if [ -n "$TERMINAL_ADDR" ]; then hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" fi - else - echo "Failed to create terminal" - exit 1 fi fi \ No newline at end of file -- cgit v1.2.3 From e9c7d8a3882b1166c0ed99e4501e465937089b05 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sun, 1 Jun 2025 08:45:47 +0530 Subject: Fixed drop down terminal focus --- config/hypr/scripts/Dropterminal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index a8e1cda3..fa5b899b 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -187,7 +187,7 @@ spawn_terminal() { count_before=$(echo "$windows_before" | jq 'length') # Launch terminal directly in special workspace to avoid visible spawn - hyprctl dispatch exec "[float; size $width $height; stayfocused; workspace special:scratchpad silent] $TERMINAL_CMD" + hyprctl dispatch exec "[float; size $width $height; workspace special:scratchpad silent] $TERMINAL_CMD" # Wait for window to appear sleep 0.1 -- cgit v1.2.3 From 30ed636ba6c51b417cccb5acde1fbb8221b55b84 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sun, 1 Jun 2025 08:48:40 +0530 Subject: SwayNC DND bypass for volume and brightness notification --- config/hypr/scripts/Brightness.sh | 2 +- config/hypr/scripts/BrightnessKbd.sh | 2 +- config/hypr/scripts/Volume.sh | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Brightness.sh b/config/hypr/scripts/Brightness.sh index 8e5d525a..bf28fc8c 100755 --- a/config/hypr/scripts/Brightness.sh +++ b/config/hypr/scripts/Brightness.sh @@ -29,7 +29,7 @@ get_icon() { # Notify notify_user() { - notify-send -e -h string:x-canonical-private-synchronous:brightness_notif -h int:value:$current -u low -i $icon "Screen" "Brightness:$current%" + notify-send -e -h string:x-canonical-private-synchronous:brightness_notif -h int:value:$current -h boolean:SWAYNC_BYPASS_DND:true -u low -i $icon "Screen" "Brightness:$current%" } # Change brightness diff --git a/config/hypr/scripts/BrightnessKbd.sh b/config/hypr/scripts/BrightnessKbd.sh index 4c56bc03..24737b73 100755 --- a/config/hypr/scripts/BrightnessKbd.sh +++ b/config/hypr/scripts/BrightnessKbd.sh @@ -26,7 +26,7 @@ get_icon() { } # Notify notify_user() { - notify-send -e -h string:x-canonical-private-synchronous:brightness_notif -h int:value:$current -u low -i "$icon" "Keyboard" "Brightness:$current%" + notify-send -e -h string:x-canonical-private-synchronous:brightness_notif -h int:value:$current -h boolean:SWAYNC_BYPASS_DND:true -u low -i "$icon" "Keyboard" "Brightness:$current%" } # Change brightness diff --git a/config/hypr/scripts/Volume.sh b/config/hypr/scripts/Volume.sh index b205f8f9..8efdb55c 100755 --- a/config/hypr/scripts/Volume.sh +++ b/config/hypr/scripts/Volume.sh @@ -32,9 +32,9 @@ get_icon() { # Notify notify_user() { if [[ "$(get_volume)" == "Muted" ]]; then - notify-send -e -h string:x-canonical-private-synchronous:volume_notif -u low -i "$(get_icon)" " Volume:" " Muted" + notify-send -e -h string:x-canonical-private-synchronous:volume_notif -h boolean:SWAYNC_BYPASS_DND:true -u low -i "$(get_icon)" " Volume:" " Muted" else - notify-send -e -h int:value:"$(get_volume | sed 's/%//')" -h string:x-canonical-private-synchronous:volume_notif -u low -i "$(get_icon)" " Volume Level:" " $(get_volume)" && + notify-send -e -h int:value:"$(get_volume | sed 's/%//')" -h string:x-canonical-private-synchronous:volume_notif -h boolean:SWAYNC_BYPASS_DND:true -u low -i "$(get_icon)" " Volume Level:" " $(get_volume)" && "$sDIR/Sounds.sh" --volume fi } @@ -60,18 +60,18 @@ dec_volume() { # Toggle Mute toggle_mute() { if [ "$(pamixer --get-mute)" == "false" ]; then - pamixer -m && notify-send -e -u low -i "$iDIR/volume-mute.png" " Mute" + pamixer -m && notify-send -e -u low -h boolean:SWAYNC_BYPASS_DND:true -i "$iDIR/volume-mute.png" " Mute" elif [ "$(pamixer --get-mute)" == "true" ]; then - pamixer -u && notify-send -e -u low -i "$(get_icon)" " Volume:" " Switched ON" + pamixer -u && notify-send -e -u low -h boolean:SWAYNC_BYPASS_DND:true -i "$(get_icon)" " Volume:" " Switched ON" fi } # Toggle Mic toggle_mic() { if [ "$(pamixer --default-source --get-mute)" == "false" ]; then - pamixer --default-source -m && notify-send -e -u low -i "$iDIR/microphone-mute.png" " Microphone:" " Switched OFF" + pamixer --default-source -m && notify-send -e -u low -h boolean:SWAYNC_BYPASS_DND:true -i "$iDIR/microphone-mute.png" " Microphone:" " Switched OFF" elif [ "$(pamixer --default-source --get-mute)" == "true" ]; then - pamixer -u --default-source u && notify-send -e -u low -i "$iDIR/microphone.png" " Microphone:" " Switched ON" + pamixer -u --default-source u && notify-send -e -u low -h boolean:SWAYNC_BYPASS_DND:true -i "$iDIR/microphone.png" " Microphone:" " Switched ON" fi } # Get Mic Icon @@ -98,7 +98,7 @@ get_mic_volume() { notify_mic_user() { volume=$(get_mic_volume) icon=$(get_mic_icon) - notify-send -e -h int:value:"$volume" -h "string:x-canonical-private-synchronous:volume_notif" -u low -i "$icon" " Mic Level:" " $volume" + notify-send -e -h int:value:"$volume" -h "string:x-canonical-private-synchronous:volume_notif" -h boolean:SWAYNC_BYPASS_DND:true -u low -i "$icon" " Mic Level:" " $volume" } # Increase MIC Volume -- cgit v1.2.3 From 952aa63147c9fb28f6ace6f0bc7ccf45ced1299a Mon Sep 17 00:00:00 2001 From: Kiran George Date: Mon, 9 Jun 2025 11:30:08 +0530 Subject: Overview v2 --- config/hypr/UserConfigs/UserKeybinds.conf | 2 +- config/hypr/UserConfigs/WindowRules.conf | 4 + config/quickshell/GlobalStates.qml | 12 + config/quickshell/config.json | 38 ++ config/quickshell/modules/common/Appearance.qml | 214 +++++++ config/quickshell/modules/common/ConfigOptions.qml | 42 ++ config/quickshell/modules/common/Directories.qml | 19 + .../modules/common/functions/color_utils.js | 85 +++ .../modules/common/functions/file_utils.js | 9 + .../modules/common/functions/fuzzysort.js | 682 +++++++++++++++++++++ .../modules/common/functions/levendist.js | 141 +++++ .../modules/common/functions/object_utils.js | 45 ++ .../modules/common/functions/string_utils.js | 188 ++++++ .../modules/common/widgets/CliphistImage.qml | 101 +++ .../modules/common/widgets/DialogButton.qml | 38 ++ .../modules/common/widgets/MaterialSymbol.qml | 30 + .../common/widgets/PointingHandInteraction.qml | 7 + .../modules/common/widgets/RippleButton.qml | 185 ++++++ .../modules/common/widgets/RoundCorner.qml | 64 ++ .../common/widgets/StyledRectangularShadow.qml | 13 + .../modules/common/widgets/StyledText.qml | 14 + .../modules/common/widgets/StyledTextArea.qml | 15 + .../modules/common/widgets/StyledToolTip.qml | 60 ++ config/quickshell/modules/overview/Overview.qml | 279 +++++++++ .../quickshell/modules/overview/OverviewWidget.qml | 341 +++++++++++ .../quickshell/modules/overview/OverviewWindow.qml | 94 +++ config/quickshell/modules/overview/SearchItem.qml | 220 +++++++ .../quickshell/modules/overview/SearchWidget.qml | 425 +++++++++++++ config/quickshell/services/AppSearch.qml | 116 ++++ config/quickshell/services/ConfigLoader.qml | 116 ++++ config/quickshell/services/HyprlandData.qml | 69 +++ config/quickshell/services/HyprlandKeybinds.qml | 73 +++ config/quickshell/services/MaterialThemeLoader.qml | 58 ++ config/quickshell/shell.qml | 27 + config/wallust/templates/qml_color.json | 21 + config/wallust/wallust.toml | 3 + 36 files changed, 3849 insertions(+), 1 deletion(-) create mode 100644 config/quickshell/GlobalStates.qml create mode 100644 config/quickshell/config.json create mode 100644 config/quickshell/modules/common/Appearance.qml create mode 100644 config/quickshell/modules/common/ConfigOptions.qml create mode 100644 config/quickshell/modules/common/Directories.qml create mode 100644 config/quickshell/modules/common/functions/color_utils.js create mode 100644 config/quickshell/modules/common/functions/file_utils.js create mode 100644 config/quickshell/modules/common/functions/fuzzysort.js create mode 100644 config/quickshell/modules/common/functions/levendist.js create mode 100644 config/quickshell/modules/common/functions/object_utils.js create mode 100644 config/quickshell/modules/common/functions/string_utils.js create mode 100644 config/quickshell/modules/common/widgets/CliphistImage.qml create mode 100644 config/quickshell/modules/common/widgets/DialogButton.qml create mode 100644 config/quickshell/modules/common/widgets/MaterialSymbol.qml create mode 100644 config/quickshell/modules/common/widgets/PointingHandInteraction.qml create mode 100644 config/quickshell/modules/common/widgets/RippleButton.qml create mode 100644 config/quickshell/modules/common/widgets/RoundCorner.qml create mode 100644 config/quickshell/modules/common/widgets/StyledRectangularShadow.qml create mode 100644 config/quickshell/modules/common/widgets/StyledText.qml create mode 100644 config/quickshell/modules/common/widgets/StyledTextArea.qml create mode 100644 config/quickshell/modules/common/widgets/StyledToolTip.qml create mode 100644 config/quickshell/modules/overview/Overview.qml create mode 100644 config/quickshell/modules/overview/OverviewWidget.qml create mode 100644 config/quickshell/modules/overview/OverviewWindow.qml create mode 100644 config/quickshell/modules/overview/SearchItem.qml create mode 100644 config/quickshell/modules/overview/SearchWidget.qml create mode 100644 config/quickshell/services/AppSearch.qml create mode 100644 config/quickshell/services/ConfigLoader.qml create mode 100644 config/quickshell/services/HyprlandData.qml create mode 100644 config/quickshell/services/HyprlandKeybinds.qml create mode 100644 config/quickshell/services/MaterialThemeLoader.qml create mode 100644 config/quickshell/shell.qml create mode 100644 config/wallust/templates/qml_color.json (limited to 'config') diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index ce501d85..c92352fc 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -18,7 +18,7 @@ source= $UserConfigs/01-UserDefaults.conf #bindr = $mainMod, $mainMod_L, exec, pkill rofi || rofi -show drun -modi drun,filebrowser,run,window # Super Key to Launch rofi menu bind = $mainMod, D, exec, pkill rofi || true && rofi -show drun -modi drun,filebrowser,run,window # Main Menu (APP Launcher) bind = $mainMod, B, exec, xdg-open "https://" # default browser -bind = $mainMod, A, exec, pkill rofi || true && ags -t 'overview' # desktop overview (if installed) +bind = $mainMod, A, global, quickshell:overviewToggle # desktop overview (if installed) bind = $mainMod, Return, exec, $term #terminal bind = $mainMod, E, exec, $files #file manager diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index 099cd2c8..4485a8f5 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -211,6 +211,10 @@ layerrule = blur, rofi layerrule = ignorezero, rofi layerrule = blur, notifications layerrule = ignorezero, notifications +layerrule = blur, quickshell:overview +layerrule = ignorezero, quickshell:overview +layerrule = ignorealpha 0.5, quickshell:overview + #layerrule = ignorealpha 0.5, tag:notif* #layerrule = ignorezero, class:^([Rr]ofi)$ diff --git a/config/quickshell/GlobalStates.qml b/config/quickshell/GlobalStates.qml new file mode 100644 index 00000000..7875645c --- /dev/null +++ b/config/quickshell/GlobalStates.qml @@ -0,0 +1,12 @@ +import "root:/modules/common/" +import QtQuick +import Quickshell +import Quickshell.Hyprland +import Quickshell.Io +pragma Singleton +pragma ComponentBehavior: Bound + +Singleton { + id: root + property bool overviewOpen: false +} \ No newline at end of file diff --git a/config/quickshell/config.json b/config/quickshell/config.json new file mode 100644 index 00000000..f98f9f69 --- /dev/null +++ b/config/quickshell/config.json @@ -0,0 +1,38 @@ +{ + "ui": { + "theme": "dark", + "scale": 1.25 + }, + "features": { + "animations": true + }, + "appearance": { + "fakeScreenRounding": 1 + }, + "overview": { + "scale": 0.15, + "numOfRows": 2, + "numOfCols": 5, + "showXwaylandIndicator": true, + "windowPadding": 6, + "position": 1 + }, + "resources": { + "updateInterval": 3000 + }, + "hacks": { + "arbitraryRaceConditionDelay": 20 + }, + "search": { + "searchEnabled": false, + "nonAppResultDelay": 30, + "prefix": { + "action": "/", + "clipboard": ";", + "emojis": ":" + } + }, + "bar": { + "bottom": false + } +} \ No newline at end of file diff --git a/config/quickshell/modules/common/Appearance.qml b/config/quickshell/modules/common/Appearance.qml new file mode 100644 index 00000000..45a4894d --- /dev/null +++ b/config/quickshell/modules/common/Appearance.qml @@ -0,0 +1,214 @@ +import QtQuick +import Quickshell +import "root:/modules/common/functions/color_utils.js" as ColorUtils +pragma Singleton +pragma ComponentBehavior: Bound + + +Singleton { + id: root + property QtObject m3colors + property QtObject animation + property QtObject animationCurves + property QtObject colors + property QtObject rounding + property QtObject font + property QtObject sizes + + property real transparency: 0.5 + property real contentTransparency: 0.1 + property real workpaceTransparency: 0.8 + // property real transparency: 0.15 + // property real contentTransparency: 0.5 + property string background_image: "file:///home/itachi/.config/rofi/.current_wallpaper" + + m3colors: QtObject { + property bool darkmode: true + property bool transparent: true + + property color m3background: "#161217" + property color m3onBackground: "#EAE0E7" + property color m3surfaceContainerLow: "#1F1A1F" + property color m3surfaceContainer: "#231E23" + property color m3surfaceContainerHigh: "#2D282E" + property color m3surfaceContainerHighest: "#383339" + property color m3onSurface: "#EAE0E7" + property color m3onSurfaceVariant: "#CFC3CD" + property color m3outline: "#cba6f7" + property color m3scrim: "#000000" + property color m3shadow: "#000000" + property color m3primary: "#E5B6F2" + property color m3primaryContainer: "#5D386A" + property color m3secondary: "#D5C0D7" + property color m3secondaryContainer: "#534457" + property color m3onPrimary: "#452152" + property color m3onPrimaryContainer: "#F9D8FF" + property color m3onSecondaryContainer: "#F2DCF3" + property color m3outlineVariant: "#4C444D" + + property color colTooltip: "#1e1e2e" + property color colOnTooltip: "#F8F9FA" + } + + colors: QtObject { + property color colSubtext: m3colors.m3outline + property color colLayer0: ColorUtils.transparentize(m3colors.m3background, root.transparency) + property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.7), root.contentTransparency); + property color colOnLayer1: m3colors.m3onSurfaceVariant; + property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55), root.contentTransparency) + property color colOnLayer2: m3colors.m3onSurface; + property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency) + property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency); + property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency) + property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency); + property color colPrimary: m3colors.m3primary + property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87) + property color colPrimaryActive: ColorUtils.mix(colors.colPrimary, colLayer1Active, 0.7) + property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7) + } + + rounding: QtObject { + property int unsharpen: 2 + property int verysmall: 8 + property int small: 12 + property int normal: 17 + property int large: 23 + property int verylarge: 30 + property int veryverylarge: 60 + property int full: 9999 + property int screenRounding: veryverylarge + property int windowRounding: veryverylarge + } + + font: QtObject { + property QtObject family: QtObject { + property string main: "Open Sans" + property string title: "JetBrains Mono NF" + property string iconMaterial: "FiraConde Nerd Font" + property string iconNerd: "SpaceMono NF" + property string monospace: "JetBrains Mono NF" + property string reading: "Readex Pro" + } + property QtObject pixelSize: QtObject { + property int smallest: 10 + property int smaller: 13 + property int small: 15 + property int normal: 16 + property int large: 17 + property int larger: 19 + property int huge: 22 + property int hugeass: 23 + property int title: 28 + } + } + + animationCurves: QtObject { + readonly property list expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1] // Default, 350ms + readonly property list expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1] // Default, 500ms + readonly property list expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1] // Default, 650ms + readonly property list expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1] // Default, 200ms + readonly property list emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1] + readonly property list emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] + readonly property list emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1] + readonly property list standard: [0.2, 0, 0, 1, 1, 1] + readonly property list standardAccel: [0.3, 0, 1, 1, 1, 1] + readonly property list standardDecel: [0, 0, 0, 1, 1, 1] + } + + animation: QtObject { + property QtObject elementMove: QtObject { + property int duration: 500 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.expressiveDefaultSpatial + property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMove.duration + easing.type: root.animation.elementMove.type + easing.bezierCurve: root.animation.elementMove.bezierCurve + } + } + property Component colorAnimation: Component { + ColorAnimation { + duration: root.animation.elementMove.duration + easing.type: root.animation.elementMove.type + easing.bezierCurve: root.animation.elementMove.bezierCurve + } + } + } + property QtObject elementMoveEnter: QtObject { + property int duration: 400 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.emphasizedDecel + property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMoveEnter.duration + easing.type: root.animation.elementMoveEnter.type + easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve + } + } + } + property QtObject elementMoveExit: QtObject { + property int duration: 200 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.emphasizedAccel + property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMoveExit.duration + easing.type: root.animation.elementMoveExit.type + easing.bezierCurve: root.animation.elementMoveExit.bezierCurve + } + } + } + property QtObject elementMoveFast: QtObject { + property int duration: 200 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.expressiveEffects + property int velocity: 850 + property Component colorAnimation: Component { ColorAnimation { + duration: root.animation.elementMoveFast.duration + easing.type: root.animation.elementMoveFast.type + easing.bezierCurve: root.animation.elementMoveFast.bezierCurve + }} + property Component numberAnimation: Component { NumberAnimation { + duration: root.animation.elementMoveFast.duration + easing.type: root.animation.elementMoveFast.type + easing.bezierCurve: root.animation.elementMoveFast.bezierCurve + }} + } + + property QtObject clickBounce: QtObject { + property int duration: 200 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.expressiveFastSpatial + property int velocity: 850 + property Component numberAnimation: Component { NumberAnimation { + duration: root.animation.clickBounce.duration + easing.type: root.animation.clickBounce.type + easing.bezierCurve: root.animation.clickBounce.bezierCurve + }} + } + property QtObject scroll: QtObject { + property int duration: 400 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.standardDecel + } + property QtObject menuDecel: QtObject { + property int duration: 350 + property int type: Easing.OutExpo + } + } + + sizes: QtObject { + property real barHeight: 40 + property real notificationPopupWidth: 410 + property real searchWidthCollapsed: 260 + property real searchWidth: 450 + property real hyprlandGapsOut: 5 + property real elevationMargin: 10 + property real fabShadowRadius: 5 + property real fabHoveredShadowRadius: 7 + } +} diff --git a/config/quickshell/modules/common/ConfigOptions.qml b/config/quickshell/modules/common/ConfigOptions.qml new file mode 100644 index 00000000..61e6ab8e --- /dev/null +++ b/config/quickshell/modules/common/ConfigOptions.qml @@ -0,0 +1,42 @@ +import QtQuick +import Quickshell +pragma Singleton +pragma ComponentBehavior: Bound + +Singleton { + + property QtObject appearance: QtObject { + property int fakeScreenRounding: 1 // 0: None | 1: Always | 2: When not fullscreen + } + + property QtObject overview: QtObject { + property real scale: 0.15 // Relative to screen size + property real numOfRows: 2 + property real numOfCols: 5 + property bool showXwaylandIndicator: true + property real windowPadding: 6 + property real position: 1 // 0: top | 1: middle | 2: bottom + } + + property QtObject resources: QtObject { + property int updateInterval: 3000 + } + + property QtObject hacks: QtObject { + property int arbitraryRaceConditionDelay: 20 // milliseconds + } + + property QtObject search: QtObject { + property bool searchEnabled: false + property int nonAppResultDelay: 30 // This prevents lagging when typing + property QtObject prefix: QtObject { + property string action: "/" + property string clipboard: ";" + property string emojis: ":" + } + } + + property QtObject bar: QtObject { + property bool bottom: false // Instead of top + } +} diff --git a/config/quickshell/modules/common/Directories.qml b/config/quickshell/modules/common/Directories.qml new file mode 100644 index 00000000..9ddf43bd --- /dev/null +++ b/config/quickshell/modules/common/Directories.qml @@ -0,0 +1,19 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import "root:/modules/common/functions/file_utils.js" as FileUtils +import Qt.labs.platform +import QtQuick +import Quickshell +import Quickshell.Hyprland + +Singleton { + // XDG Dirs, with "file://" + readonly property string config: StandardPaths.standardLocations(StandardPaths.ConfigLocation)[0] + readonly property string state: StandardPaths.standardLocations(StandardPaths.StateLocation)[0] + + // Other dirs used by the shell, without "file://" + property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/quickshell`) + property string shellConfigPath: `${Directories.shellConfig}/config.json` + property string generatedMaterialThemePath: `${Directories.shellConfig}/qml_color.json` +} diff --git a/config/quickshell/modules/common/functions/color_utils.js b/config/quickshell/modules/common/functions/color_utils.js new file mode 100644 index 00000000..c0ccfda9 --- /dev/null +++ b/config/quickshell/modules/common/functions/color_utils.js @@ -0,0 +1,85 @@ +// This module provides high level utility functions for color manipulation. + +/** + * Returns a color with the hue of color2 and the saturation, value, and alpha of color1. + * + * @param {string} color1 - The base color (any Qt.color-compatible string). + * @param {string} color2 - The color to take hue from. + * @returns {Qt.rgba} The resulting color. + */ +function colorWithHueOf(color1, color2) { + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + // Qt.color hsvHue/hsvSaturation/hsvValue/alpha return 0-1 + var hue = c2.hsvHue; + var sat = c1.hsvSaturation; + var val = c1.hsvValue; + var alpha = c1.a; + + return Qt.hsva(hue, sat, val, alpha); +} + +/** + * Returns a color with the saturation of color2 and the hue/value/alpha of color1. + * + * @param {string} color1 - The base color (any Qt.color-compatible string). + * @param {string} color2 - The color to take saturation from. + * @returns {Qt.rgba} The resulting color. + */ +function colorWithSaturationOf(color1, color2) { + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + var hue = c1.hsvHue; + var sat = c2.hsvSaturation; + var val = c1.hsvValue; + var alpha = c1.a; + + return Qt.hsva(hue, sat, val, alpha); +} + +/** + * Adapts color1 to the accent (hue and saturation) of color2 using HSL, keeping lightness and alpha from color1. + * + * @param {string} color1 - The base color (any Qt.color-compatible string). + * @param {string} color2 - The accent color. + * @returns {Qt.rgba} The resulting color. + */ +function adaptToAccent(color1, color2) { + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + var hue = c2.hslHue; + var sat = c2.hslSaturation; + var light = c1.hslLightness; + var alpha = c1.a; + + return Qt.hsla(hue, sat, light, alpha); +} + +/** + * Mixes two colors by a given percentage. + * + * @param {string} color1 - The first color (any Qt.color-compatible string). + * @param {string} color2 - The second color. + * @param {number} percentage - The mix ratio (0-1). 1 = all color1, 0 = all color2. + * @returns {Qt.rgba} The resulting mixed color. + */ +function mix(color1, color2, percentage) { + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + return Qt.rgba(percentage * c1.r + (1 - percentage) * c2.r, percentage * c1.g + (1 - percentage) * c2.g, percentage * c1.b + (1 - percentage) * c2.b, percentage * c1.a + (1 - percentage) * c2.a); +} + +/** + * Transparentizes a color by a given percentage. + * + * @param {string} color - The color (any Qt.color-compatible string). + * @param {number} percentage - The amount to transparentize (0-1). + * @returns {Qt.rgba} The resulting color. + */ +function transparentize(color, percentage = 1) { + var c = Qt.color(color); + return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage)); +} diff --git a/config/quickshell/modules/common/functions/file_utils.js b/config/quickshell/modules/common/functions/file_utils.js new file mode 100644 index 00000000..758950de --- /dev/null +++ b/config/quickshell/modules/common/functions/file_utils.js @@ -0,0 +1,9 @@ +/** + * Trims the File protocol off the input string + * @param {string} str + * @returns {string} + */ +function trimFileProtocol(str) { + return str.startsWith("file://") ? str.slice(7) : str; +} + diff --git a/config/quickshell/modules/common/functions/fuzzysort.js b/config/quickshell/modules/common/functions/fuzzysort.js new file mode 100644 index 00000000..1c1d9b9d --- /dev/null +++ b/config/quickshell/modules/common/functions/fuzzysort.js @@ -0,0 +1,682 @@ +.pragma library + +// https://github.com/farzher/fuzzysort +// License: MIT | Copyright (c) 2018 Stephen Kamenar +// A copy of the license is available in the `licenses` folder of this repository + +var single = (search, target) => { + if(!search || !target) return NULL + + var preparedSearch = getPreparedSearch(search) + if(!isPrepared(target)) target = getPrepared(target) + + var searchBitflags = preparedSearch.bitflags + if((searchBitflags & target._bitflags) !== searchBitflags) return NULL + + return algorithm(preparedSearch, target) +} + +var go = (search, targets, options) => { + if(!search) return options?.all ? all(targets, options) : noResults + + var preparedSearch = getPreparedSearch(search) + var searchBitflags = preparedSearch.bitflags + var containsSpace = preparedSearch.containsSpace + + var threshold = denormalizeScore( options?.threshold || 0 ) + var limit = options?.limit || INFINITY + + var resultsLen = 0; var limitedCount = 0 + var targetsLen = targets.length + + function push_result(result) { + if(resultsLen < limit) { q.add(result); ++resultsLen } + else { + ++limitedCount + if(result._score > q.peek()._score) q.replaceTop(result) + } + } + + // This code is copy/pasted 3 times for performance reasons [options.key, options.keys, no keys] + + // options.key + if(options?.key) { + var key = options.key + for(var i = 0; i < targetsLen; ++i) { var obj = targets[i] + var target = getValue(obj, key) + if(!target) continue + if(!isPrepared(target)) target = getPrepared(target) + + if((searchBitflags & target._bitflags) !== searchBitflags) continue + var result = algorithm(preparedSearch, target) + if(result === NULL) continue + if(result._score < threshold) continue + + result.obj = obj + push_result(result) + } + + // options.keys + } else if(options?.keys) { + var keys = options.keys + var keysLen = keys.length + + outer: for(var i = 0; i < targetsLen; ++i) { var obj = targets[i] + + { // early out based on bitflags + var keysBitflags = 0 + for (var keyI = 0; keyI < keysLen; ++keyI) { + var key = keys[keyI] + var target = getValue(obj, key) + if(!target) { tmpTargets[keyI] = noTarget; continue } + if(!isPrepared(target)) target = getPrepared(target) + tmpTargets[keyI] = target + + keysBitflags |= target._bitflags + } + + if((searchBitflags & keysBitflags) !== searchBitflags) continue + } + + if(containsSpace) for(let i=0; i -1000) { + if(keysSpacesBestScores[i] > NEGATIVE_INFINITY) { + var tmp = (keysSpacesBestScores[i] + allowPartialMatchScores[i]) / 4/*bonus score for having multiple matches*/ + if(tmp > keysSpacesBestScores[i]) keysSpacesBestScores[i] = tmp + } + } + if(allowPartialMatchScores[i] > keysSpacesBestScores[i]) keysSpacesBestScores[i] = allowPartialMatchScores[i] + } + } + + if(containsSpace) { + for(let i=0; i -1000) { + if(score > NEGATIVE_INFINITY) { + var tmp = (score + result._score) / 4/*bonus score for having multiple matches*/ + if(tmp > score) score = tmp + } + } + if(result._score > score) score = result._score + } + } + + objResults.obj = obj + objResults._score = score + if(options?.scoreFn) { + score = options.scoreFn(objResults) + if(!score) continue + score = denormalizeScore(score) + objResults._score = score + } + + if(score < threshold) continue + push_result(objResults) + } + + // no keys + } else { + for(var i = 0; i < targetsLen; ++i) { var target = targets[i] + if(!target) continue + if(!isPrepared(target)) target = getPrepared(target) + + if((searchBitflags & target._bitflags) !== searchBitflags) continue + var result = algorithm(preparedSearch, target) + if(result === NULL) continue + if(result._score < threshold) continue + + push_result(result) + } + } + + if(resultsLen === 0) return noResults + var results = new Array(resultsLen) + for(var i = resultsLen - 1; i >= 0; --i) results[i] = q.poll() + results.total = resultsLen + limitedCount + return results +} + + +// this is written as 1 function instead of 2 for minification. perf seems fine ... +// except when minified. the perf is very slow +var highlight = (result, open='', close='') => { + var callback = typeof open === 'function' ? open : undefined + + var target = result.target + var targetLen = target.length + var indexes = result.indexes + var highlighted = '' + var matchI = 0 + var indexesI = 0 + var opened = false + var parts = [] + + for(var i = 0; i < targetLen; ++i) { var char = target[i] + if(indexes[indexesI] === i) { + ++indexesI + if(!opened) { opened = true + if(callback) { + parts.push(highlighted); highlighted = '' + } else { + highlighted += open + } + } + + if(indexesI === indexes.length) { + if(callback) { + highlighted += char + parts.push(callback(highlighted, matchI++)); highlighted = '' + parts.push(target.substr(i+1)) + } else { + highlighted += char + close + target.substr(i+1) + } + break + } + } else { + if(opened) { opened = false + if(callback) { + parts.push(callback(highlighted, matchI++)); highlighted = '' + } else { + highlighted += close + } + } + } + highlighted += char + } + + return callback ? parts : highlighted +} + + +var prepare = (target) => { + if(typeof target === 'number') target = ''+target + else if(typeof target !== 'string') target = '' + var info = prepareLowerInfo(target) + return new_result(target, {_targetLower:info._lower, _targetLowerCodes:info.lowerCodes, _bitflags:info.bitflags}) +} + +var cleanup = () => { preparedCache.clear(); preparedSearchCache.clear() } + + +// Below this point is only internal code +// Below this point is only internal code +// Below this point is only internal code +// Below this point is only internal code + + +class Result { + get ['indexes']() { return this._indexes.slice(0, this._indexes.len).sort((a,b)=>a-b) } + set ['indexes'](indexes) { return this._indexes = indexes } + ['highlight'](open, close) { return highlight(this, open, close) } + get ['score']() { return normalizeScore(this._score) } + set ['score'](score) { this._score = denormalizeScore(score) } +} + +class KeysResult extends Array { + get ['score']() { return normalizeScore(this._score) } + set ['score'](score) { this._score = denormalizeScore(score) } +} + +var new_result = (target, options) => { + const result = new Result() + result['target'] = target + result['obj'] = options.obj ?? NULL + result._score = options._score ?? NEGATIVE_INFINITY + result._indexes = options._indexes ?? [] + result._targetLower = options._targetLower ?? '' + result._targetLowerCodes = options._targetLowerCodes ?? NULL + result._nextBeginningIndexes = options._nextBeginningIndexes ?? NULL + result._bitflags = options._bitflags ?? 0 + return result +} + + +var normalizeScore = score => { + if(score === NEGATIVE_INFINITY) return 0 + if(score > 1) return score + return Math.E ** ( ((-score + 1)**.04307 - 1) * -2) +} +var denormalizeScore = normalizedScore => { + if(normalizedScore === 0) return NEGATIVE_INFINITY + if(normalizedScore > 1) return normalizedScore + return 1 - Math.pow((Math.log(normalizedScore) / -2 + 1), 1 / 0.04307) +} + + +var prepareSearch = (search) => { + if(typeof search === 'number') search = ''+search + else if(typeof search !== 'string') search = '' + search = search.trim() + var info = prepareLowerInfo(search) + + var spaceSearches = [] + if(info.containsSpace) { + var searches = search.split(/\s+/) + searches = [...new Set(searches)] // distinct + for(var i=0; i { + if(target.length > 999) return prepare(target) // don't cache huge targets + var targetPrepared = preparedCache.get(target) + if(targetPrepared !== undefined) return targetPrepared + targetPrepared = prepare(target) + preparedCache.set(target, targetPrepared) + return targetPrepared +} +var getPreparedSearch = (search) => { + if(search.length > 999) return prepareSearch(search) // don't cache huge searches + var searchPrepared = preparedSearchCache.get(search) + if(searchPrepared !== undefined) return searchPrepared + searchPrepared = prepareSearch(search) + preparedSearchCache.set(search, searchPrepared) + return searchPrepared +} + + +var all = (targets, options) => { + var results = []; results.total = targets.length // this total can be wrong if some targets are skipped + + var limit = options?.limit || INFINITY + + if(options?.key) { + for(var i=0;i= limit) return results + } + } else if(options?.keys) { + for(var i=0;i= 0; --keyI) { + var target = getValue(obj, options.keys[keyI]) + if(!target) { objResults[keyI] = noTarget; continue } + if(!isPrepared(target)) target = getPrepared(target) + target._score = NEGATIVE_INFINITY + target._indexes.len = 0 + objResults[keyI] = target + } + objResults.obj = obj + objResults._score = NEGATIVE_INFINITY + results.push(objResults); if(results.length >= limit) return results + } + } else { + for(var i=0;i= limit) return results + } + } + + return results +} + + +var algorithm = (preparedSearch, prepared, allowSpaces=false, allowPartialMatch=false) => { + if(allowSpaces===false && preparedSearch.containsSpace) return algorithmSpaces(preparedSearch, prepared, allowPartialMatch) + + var searchLower = preparedSearch._lower + var searchLowerCodes = preparedSearch.lowerCodes + var searchLowerCode = searchLowerCodes[0] + var targetLowerCodes = prepared._targetLowerCodes + var searchLen = searchLowerCodes.length + var targetLen = targetLowerCodes.length + var searchI = 0 // where we at + var targetI = 0 // where you at + var matchesSimpleLen = 0 + + // very basic fuzzy match; to remove non-matching targets ASAP! + // walk through target. find sequential matches. + // if all chars aren't found then exit + for(;;) { + var isMatch = searchLowerCode === targetLowerCodes[targetI] + if(isMatch) { + matchesSimple[matchesSimpleLen++] = targetI + ++searchI; if(searchI === searchLen) break + searchLowerCode = searchLowerCodes[searchI] + } + ++targetI; if(targetI >= targetLen) return NULL // Failed to find searchI + } + + var searchI = 0 + var successStrict = false + var matchesStrictLen = 0 + + var nextBeginningIndexes = prepared._nextBeginningIndexes + if(nextBeginningIndexes === NULL) nextBeginningIndexes = prepared._nextBeginningIndexes = prepareNextBeginningIndexes(prepared.target) + targetI = matchesSimple[0]===0 ? 0 : nextBeginningIndexes[matchesSimple[0]-1] + + // Our target string successfully matched all characters in sequence! + // Let's try a more advanced and strict test to improve the score + // only count it as a match if it's consecutive or a beginning character! + var backtrackCount = 0 + if(targetI !== targetLen) for(;;) { + if(targetI >= targetLen) { + // We failed to find a good spot for this search char, go back to the previous search char and force it forward + if(searchI <= 0) break // We failed to push chars forward for a better match + + ++backtrackCount; if(backtrackCount > 200) break // exponential backtracking is taking too long, just give up and return a bad match + + --searchI + var lastMatch = matchesStrict[--matchesStrictLen] + targetI = nextBeginningIndexes[lastMatch] + + } else { + var isMatch = searchLowerCodes[searchI] === targetLowerCodes[targetI] + if(isMatch) { + matchesStrict[matchesStrictLen++] = targetI + ++searchI; if(searchI === searchLen) { successStrict = true; break } + ++targetI + } else { + targetI = nextBeginningIndexes[targetI] + } + } + } + + // check if it's a substring match + var substringIndex = searchLen <= 1 ? -1 : prepared._targetLower.indexOf(searchLower, matchesSimple[0]) // perf: this is slow + var isSubstring = !!~substringIndex + var isSubstringBeginning = !isSubstring ? false : substringIndex===0 || prepared._nextBeginningIndexes[substringIndex-1] === substringIndex + + // if it's a substring match but not at a beginning index, let's try to find a substring starting at a beginning index for a better score + if(isSubstring && !isSubstringBeginning) { + for(var i=0; i { + var score = 0 + + var extraMatchGroupCount = 0 + for(var i = 1; i < searchLen; ++i) { + if(matches[i] - matches[i-1] !== 1) {score -= matches[i]; ++extraMatchGroupCount} + } + var unmatchedDistance = matches[searchLen-1] - matches[0] - (searchLen-1) + + score -= (12+unmatchedDistance) * extraMatchGroupCount // penality for more groups + + if(matches[0] !== 0) score -= matches[0]*matches[0]*.2 // penality for not starting near the beginning + + if(!successStrict) { + score *= 1000 + } else { + // successStrict on a target with too many beginning indexes loses points for being a bad target + var uniqueBeginningIndexes = 1 + for(var i = nextBeginningIndexes[0]; i < targetLen; i=nextBeginningIndexes[i]) ++uniqueBeginningIndexes + + if(uniqueBeginningIndexes > 24) score *= (uniqueBeginningIndexes-24)*10 // quite arbitrary numbers here ... + } + + score -= (targetLen - searchLen)/2 // penality for longer targets + + if(isSubstring) score /= 1+searchLen*searchLen*1 // bonus for being a full substring + if(isSubstringBeginning) score /= 1+searchLen*searchLen*1 // bonus for substring starting on a beginningIndex + + score -= (targetLen - searchLen)/2 // penality for longer targets + + return score + } + + if(!successStrict) { + if(isSubstring) for(var i=0; i { + var seen_indexes = new Set() + var score = 0 + var result = NULL + + var first_seen_index_last_search = 0 + var searches = preparedSearch.spaceSearches + var searchesLen = searches.length + var changeslen = 0 + + // Return _nextBeginningIndexes back to its normal state + var resetNextBeginningIndexes = () => { + for(let i=changeslen-1; i>=0; i--) target._nextBeginningIndexes[nextBeginningIndexesChanges[i*2 + 0]] = nextBeginningIndexesChanges[i*2 + 1] + } + + var hasAtLeast1Match = false + for(var i=0; i=0; i--) { + if(toReplace !== target._nextBeginningIndexes[i]) break + target._nextBeginningIndexes[i] = newBeginningIndex + nextBeginningIndexesChanges[changeslen*2 + 0] = i + nextBeginningIndexesChanges[changeslen*2 + 1] = toReplace + changeslen++ + } + } + } + + score += result._score / searchesLen + allowPartialMatchScores[i] = result._score / searchesLen + + // dock points based on order otherwise "c man" returns Manifest.cpp instead of CheatManager.h + if(result._indexes[0] < first_seen_index_last_search) { + score -= (first_seen_index_last_search - result._indexes[0]) * 2 + } + first_seen_index_last_search = result._indexes[0] + + for(var j=0; j score) { + if(allowPartialMatch) { + for(var i=0; i str.replace(/\p{Script=Latin}+/gu, match => match.normalize('NFD')).replace(/[\u0300-\u036f]/g, '') + +var prepareLowerInfo = (str) => { + str = remove_accents(str) + var strLen = str.length + var lower = str.toLowerCase() + var lowerCodes = [] // new Array(strLen) sparse array is too slow + var bitflags = 0 + var containsSpace = false // space isn't stored in bitflags because of how searching with a space works + + for(var i = 0; i < strLen; ++i) { + var lowerCode = lowerCodes[i] = lower.charCodeAt(i) + + if(lowerCode === 32) { + containsSpace = true + continue // it's important that we don't set any bitflags for space + } + + var bit = lowerCode>=97&&lowerCode<=122 ? lowerCode-97 // alphabet + : lowerCode>=48&&lowerCode<=57 ? 26 // numbers + // 3 bits available + : lowerCode<=127 ? 30 // other ascii + : 31 // other utf8 + bitflags |= 1< { + var targetLen = target.length + var beginningIndexes = []; var beginningIndexesLen = 0 + var wasUpper = false + var wasAlphanum = false + for(var i = 0; i < targetLen; ++i) { + var targetCode = target.charCodeAt(i) + var isUpper = targetCode>=65&&targetCode<=90 + var isAlphanum = isUpper || targetCode>=97&&targetCode<=122 || targetCode>=48&&targetCode<=57 + var isBeginning = isUpper && !wasUpper || !wasAlphanum || !isAlphanum + wasUpper = isUpper + wasAlphanum = isAlphanum + if(isBeginning) beginningIndexes[beginningIndexesLen++] = i + } + return beginningIndexes +} +var prepareNextBeginningIndexes = (target) => { + target = remove_accents(target) + var targetLen = target.length + var beginningIndexes = prepareBeginningIndexes(target) + var nextBeginningIndexes = [] // new Array(targetLen) sparse array is too slow + var lastIsBeginning = beginningIndexes[0] + var lastIsBeginningI = 0 + for(var i = 0; i < targetLen; ++i) { + if(lastIsBeginning > i) { + nextBeginningIndexes[i] = lastIsBeginning + } else { + lastIsBeginning = beginningIndexes[++lastIsBeginningI] + nextBeginningIndexes[i] = lastIsBeginning===undefined ? targetLen : lastIsBeginning + } + } + return nextBeginningIndexes +} + +var preparedCache = new Map() +var preparedSearchCache = new Map() + +// the theory behind these being globals is to reduce garbage collection by not making new arrays +var matchesSimple = []; var matchesStrict = [] +var nextBeginningIndexesChanges = [] // allows straw berry to match strawberry well, by modifying the end of a substring to be considered a beginning index for the rest of the search +var keysSpacesBestScores = []; var allowPartialMatchScores = [] +var tmpTargets = []; var tmpResults = [] + +// prop = 'key' 2.5ms optimized for this case, seems to be about as fast as direct obj[prop] +// prop = 'key1.key2' 10ms +// prop = ['key1', 'key2'] 27ms +// prop = obj => obj.tags.join() ??ms +var getValue = (obj, prop) => { + var tmp = obj[prop]; if(tmp !== undefined) return tmp + if(typeof prop === 'function') return prop(obj) // this should run first. but that makes string props slower + var segs = prop + if(!Array.isArray(prop)) segs = prop.split('.') + var len = segs.length + var i = -1 + while (obj && (++i < len)) obj = obj[segs[i]] + return obj +} + +var isPrepared = (x) => { return typeof x === 'object' && typeof x._bitflags === 'number' } +var INFINITY = Infinity; var NEGATIVE_INFINITY = -INFINITY +var noResults = []; noResults.total = 0 +var NULL = null + +var noTarget = prepare('') + +// Hacked version of https://github.com/lemire/FastPriorityQueue.js +var fastpriorityqueue=r=>{var e=[],o=0,a={},v=r=>{for(var a=0,v=e[a],c=1;c>1]=e[a],c=1+(a<<1)}for(var f=a-1>>1;a>0&&v._score>1)e[a]=e[f];e[a]=v};return a.add=(r=>{var a=o;e[o++]=r;for(var v=a-1>>1;a>0&&r._score>1)e[a]=e[v];e[a]=r}),a.poll=(r=>{if(0!==o){var a=e[0];return e[0]=e[--o],v(),a}}),a.peek=(r=>{if(0!==o)return e[0]}),a.replaceTop=(r=>{e[0]=r,v()}),a} +var q = fastpriorityqueue() // reuse this diff --git a/config/quickshell/modules/common/functions/levendist.js b/config/quickshell/modules/common/functions/levendist.js new file mode 100644 index 00000000..90180d21 --- /dev/null +++ b/config/quickshell/modules/common/functions/levendist.js @@ -0,0 +1,141 @@ +// Original code from https://github.com/koeqaife/hyprland-material-you +// Original code license: GPLv3 +// Translated to Js from Cython with an LLM and reviewed + +function min3(a, b, c) { + return a < b && a < c ? a : b < c ? b : c; +} + +function max3(a, b, c) { + return a > b && a > c ? a : b > c ? b : c; +} + +function min2(a, b) { + return a < b ? a : b; +} + +function max2(a, b) { + return a > b ? a : b; +} + +function levenshteinDistance(s1, s2) { + let len1 = s1.length; + let len2 = s2.length; + + if (len1 === 0) return len2; + if (len2 === 0) return len1; + + if (len2 > len1) { + [s1, s2] = [s2, s1]; + [len1, len2] = [len2, len1]; + } + + let prev = new Array(len2 + 1); + let curr = new Array(len2 + 1); + + for (let j = 0; j <= len2; j++) { + prev[j] = j; + } + + for (let i = 1; i <= len1; i++) { + curr[0] = i; + for (let j = 1; j <= len2; j++) { + let cost = s1[i - 1] === s2[j - 1] ? 0 : 1; + curr[j] = min3(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost); + } + [prev, curr] = [curr, prev]; + } + + return prev[len2]; +} + +function partialRatio(shortS, longS) { + let lenS = shortS.length; + let lenL = longS.length; + let best = 0.0; + + if (lenS === 0) return 1.0; + + for (let i = 0; i <= lenL - lenS; i++) { + let sub = longS.slice(i, i + lenS); + let dist = levenshteinDistance(shortS, sub); + let score = 1.0 - (dist / lenS); + if (score > best) best = score; + } + + return best; +} + +function computeScore(s1, s2) { + if (s1 === s2) return 1.0; + + let dist = levenshteinDistance(s1, s2); + let maxLen = max2(s1.length, s2.length); + if (maxLen === 0) return 1.0; + + let full = 1.0 - (dist / maxLen); + let part = s1.length < s2.length ? partialRatio(s1, s2) : partialRatio(s2, s1); + + let score = 0.85 * full + 0.15 * part; + + if (s1 && s2 && s1[0] !== s2[0]) { + score -= 0.05; + } + + let lenDiff = Math.abs(s1.length - s2.length); + if (lenDiff >= 3) { + score -= 0.05 * lenDiff / maxLen; + } + + let commonPrefixLen = 0; + let minLen = min2(s1.length, s2.length); + for (let i = 0; i < minLen; i++) { + if (s1[i] === s2[i]) { + commonPrefixLen++; + } else { + break; + } + } + score += 0.02 * commonPrefixLen; + + if (s1.includes(s2) || s2.includes(s1)) { + score += 0.06; + } + + return Math.max(0.0, Math.min(1.0, score)); +} + +function computeTextMatchScore(s1, s2) { + if (s1 === s2) return 1.0; + + let dist = levenshteinDistance(s1, s2); + let maxLen = max2(s1.length, s2.length); + if (maxLen === 0) return 1.0; + + let full = 1.0 - (dist / maxLen); + let part = s1.length < s2.length ? partialRatio(s1, s2) : partialRatio(s2, s1); + + let score = 0.4 * full + 0.6 * part; + + let lenDiff = Math.abs(s1.length - s2.length); + if (lenDiff >= 10) { + score -= 0.02 * lenDiff / maxLen; + } + + let commonPrefixLen = 0; + let minLen = min2(s1.length, s2.length); + for (let i = 0; i < minLen; i++) { + if (s1[i] === s2[i]) { + commonPrefixLen++; + } else { + break; + } + } + score += 0.01 * commonPrefixLen; + + if (s1.includes(s2) || s2.includes(s1)) { + score += 0.2; + } + + return Math.max(0.0, Math.min(1.0, score)); +} diff --git a/config/quickshell/modules/common/functions/object_utils.js b/config/quickshell/modules/common/functions/object_utils.js new file mode 100644 index 00000000..96c632fd --- /dev/null +++ b/config/quickshell/modules/common/functions/object_utils.js @@ -0,0 +1,45 @@ +function toPlainObject(qtObj) { + if (qtObj === null || typeof qtObj !== "object") return qtObj; + + // Handle arrays + if (Array.isArray(qtObj)) { + return qtObj.map(toPlainObject); + } + + const result = ({}); + for (let key in qtObj) { + if ( + typeof qtObj[key] !== "function" && + !key.startsWith("objectName") && + !key.startsWith("children") && + !key.startsWith("object") && + !key.startsWith("parent") && + !key.startsWith("metaObject") && + !key.startsWith("destroyed") && + !key.startsWith("reloadableId") + ) { + result[key] = toPlainObject(qtObj[key]); + } + } + return result; +} + +function applyToQtObject(qtObj, jsonObj) { + if (!qtObj || typeof jsonObj !== "object" || jsonObj === null) return; + + for (let key in jsonObj) { + if (!qtObj.hasOwnProperty(key)) continue; + + // Check if the property is a QtObject (not a value) + const value = qtObj[key]; + const jsonValue = jsonObj[key]; + + // If it's an object and not an array, recurse + if (value && typeof value === "object" && !Array.isArray(value)) { + applyToQtObject(value, jsonValue); + } else { + // Otherwise, assign the value + qtObj[key] = jsonValue; + } + } +} diff --git a/config/quickshell/modules/common/functions/string_utils.js b/config/quickshell/modules/common/functions/string_utils.js new file mode 100644 index 00000000..c22671eb --- /dev/null +++ b/config/quickshell/modules/common/functions/string_utils.js @@ -0,0 +1,188 @@ +/** + * Formats a string according to the args that are passed in + * @param { string } str + * @param {...any} args + * @returns + */ +function format(str, ...args) { + return str.replace(/{(\d+)}/g, (match, index) => + typeof args[index] !== 'undefined' ? args[index] : match + ); +} + +/** + * Returns the domain of the passed in url or null + * @param { string } url + * @returns { string| null } + */ +function getDomain(url) { + const match = url.match(/^(?:https?:\/\/)?(?:www\.)?([^\/]+)/); + return match ? match[1] : null; +} + +/** + * Returns the base url of the passed in url or null + * @param { string } url + * @returns { string | null } + */ +function getBaseUrl(url) { + const match = url.match(/^(https?:\/\/[^\/]+)(\/.*)?$/); + return match ? match[1] : null; +} + +/** + * Escapes single quotes in shell commands + * @param { string } str + * @returns { string } + */ +function shellSingleQuoteEscape(str) { + // escape single quotes + return String(str) + // .replace(/\\/g, '\\\\') + .replace(/'/g, "'\\''"); +} + +/** + * Splits markdown blocks into three different types: text, think, and code. + * @param { string } markdown + */ +function splitMarkdownBlocks(markdown) { + const regex = /```(\w+)?\n([\s\S]*?)```|([\s\S]*?)<\/think>/g; + /** + * @type {{type: "text" | "think" | "code"; content: string; lang: string | undefined; completed: boolean | undefined}[]} + */ + let result = []; + let lastIndex = 0; + let match; + while ((match = regex.exec(markdown)) !== null) { + if (match.index > lastIndex) { + const text = markdown.slice(lastIndex, match.index); + if (text.trim()) { + result.push({ type: "text", content: text }); + } + } + if (match[0].startsWith('```')) { + if (match[2] && match[2].trim()) { + result.push({ type: "code", lang: match[1] || "", content: match[2], completed: true }); + } + } else if (match[0].startsWith('')) { + if (match[3] && match[3].trim()) { + result.push({ type: "think", content: match[3], completed: true }); + } + } + lastIndex = regex.lastIndex; + } + // Handle any remaining text after the last match + if (lastIndex < markdown.length) { + const text = markdown.slice(lastIndex); + // Check for unfinished block + const thinkStart = text.indexOf(''); + const codeStart = text.indexOf('```'); + if ( + thinkStart !== -1 && + (codeStart === -1 || thinkStart < codeStart) + ) { + const beforeThink = text.slice(0, thinkStart); + if (beforeThink.trim()) { + result.push({ type: "text", content: beforeThink }); + } + const thinkContent = text.slice(thinkStart + 7); + if (thinkContent.trim()) { + result.push({ type: "think", content: thinkContent, completed: false }); + } + } else if (codeStart !== -1) { + const beforeCode = text.slice(0, codeStart); + if (beforeCode.trim()) { + result.push({ type: "text", content: beforeCode }); + } + // Try to detect language after ``` + const codeLangMatch = text.slice(codeStart + 3).match(/^(\w+)?\n/); + let lang = ""; + let codeContentStart = codeStart + 3; + if (codeLangMatch) { + lang = codeLangMatch[1] || ""; + codeContentStart += codeLangMatch[0].length; + } else if (text[codeStart + 3] === '\n') { + codeContentStart += 1; + } + const codeContent = text.slice(codeContentStart); + if (codeContent.trim()) { + result.push({ type: "code", lang, content: codeContent, completed: false }); + } + } else if (text.trim()) { + result.push({ type: "text", content: text }); + } + } + // console.log(JSON.stringify(result, null, 2)); + return result; +} + +/** + * Returns the original string with backslashes escaped + * @param { string } str + * @returns { string } + */ +function escapeBackslashes(str) { + return str.replace(/\\/g, '\\\\'); +} + +/** + * Wraps words to supplied maximum length + * @param { string | null } str + * @param { number } maxLen + * @returns { string } + */ +function wordWrap(str, maxLen) { + if (!str) return ""; + let words = str.split(" "); + let lines = []; + let current = ""; + for (let i = 0; i < words.length; ++i) { + if ((current + (current.length > 0 ? " " : "") + words[i]).length > maxLen) { + if (current.length > 0) lines.push(current); + current = words[i]; + } else { + current += (current.length > 0 ? " " : "") + words[i]; + } + } + if (current.length > 0) lines.push(current); + return lines.join("\n"); +} + +function cleanMusicTitle(title) { + if (!title) return ""; + // Brackets + title = title.replace(/^ *\([^)]*\) */g, " "); // Round brackets + title = title.replace(/^ *\[[^\]]*\] */g, " "); // Square brackets + title = title.replace(/^ *\{[^\}]*\} */g, " "); // Curly brackets + // Japenis brackets + title = title.replace(/^ *【[^】]*】/, "") // Touhou + title = title.replace(/^ *《[^》]*》/, "") // ?? + title = title.replace(/^ *「[^」]*」/, "") // OP/ED + title = title.replace(/^ *『[^』]*』/, "") // OP/ED + + return title; +} + +function friendlyTimeForSeconds(seconds) { + if (isNaN(seconds) || seconds < 0) return "0:00"; + seconds = Math.floor(seconds); + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + const s = seconds % 60; + if (h > 0) { + return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; + } else { + return `${m}:${s.toString().padStart(2, '0')}`; + } +} + +function escapeHtml(str) { + if (typeof str !== 'string') return str; + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} diff --git a/config/quickshell/modules/common/widgets/CliphistImage.qml b/config/quickshell/modules/common/widgets/CliphistImage.qml new file mode 100644 index 00000000..9de34450 --- /dev/null +++ b/config/quickshell/modules/common/widgets/CliphistImage.qml @@ -0,0 +1,101 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/services" +import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/file_utils.js" as FileUtils +import Qt5Compat.GraphicalEffects +import Qt.labs.platform +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Io +import Quickshell.Widgets +import Quickshell.Hyprland + +Rectangle { + id: root + property string entry + property real maxWidth + property real maxHeight + + property string imageDecodePath: Directories.cliphistDecode + property string imageDecodeFileName: `${entryNumber}` + property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}` + property string source + + property int entryNumber: { + if (!root.entry) return 0 + const match = root.entry.match(/^(\d+)\t/) + return match ? parseInt(match[1]) : 0 + } + property int imageWidth: { + if (!root.entry) return 0 + const match = root.entry.match(/(\d+)x(\d+)/) + return match ? parseInt(match[1]) : 0 + } + property int imageHeight: { + if (!root.entry) return 0 + const match = root.entry.match(/(\d+)x(\d+)/) + return match ? parseInt(match[2]) : 0 + } + property real scale: { + return Math.min( + root.maxWidth / imageWidth, + root.maxHeight / imageHeight, + 1 + ) + } + + color: Appearance.colors.colLayer1 + radius: Appearance.rounding.small + implicitHeight: imageHeight * scale + implicitWidth: imageWidth * scale + + Component.onCompleted: { + decodeImageProcess.running = true + } + + Process { + id: decodeImageProcess + command: ["bash", "-c", + `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | cliphist decode > '${imageDecodeFilePath}'` + ] + onExited: (exitCode, exitStatus) => { + if (exitCode === 0) { + root.source = imageDecodeFilePath + } else { + console.error("[CliphistImage] Failed to decode image for entry:", root.entry) + root.source = "" + } + } + } + + Component.onDestruction: { + Hyprland.dispatch(`exec bash -c "[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'"`) + } + + Image { + id: image + anchors.fill: parent + + source: Qt.resolvedUrl(root.source) + fillMode: Image.PreserveAspectFit + antialiasing: true + asynchronous: true + + width: root.imageWidth * root.scale + height: root.imageHeight * root.scale + sourceSize.width: width + sourceSize.height: height + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: image.width + height: image.height + radius: root.radius + } + } + } +} + diff --git a/config/quickshell/modules/common/widgets/DialogButton.qml b/config/quickshell/modules/common/widgets/DialogButton.qml new file mode 100644 index 00000000..9e19a507 --- /dev/null +++ b/config/quickshell/modules/common/widgets/DialogButton.qml @@ -0,0 +1,38 @@ +import "root:/modules/common" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io + +/** + * Material 3 dialog button. See https://m3.material.io/components/dialogs/overview + */ +RippleButton { + id: button + + property string buttonText + implicitHeight: 30 + implicitWidth: buttonTextWidget.implicitWidth + 15 * 2 + buttonRadius: Appearance?.rounding.full ?? 9999 + + property color colEnabled: Appearance?.colors.colPrimary + property color colDisabled: Appearance?.m3colors.m3outline + + contentItem: StyledText { + id: buttonTextWidget + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + text: buttonText + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Appearance?.font.pixelSize.small ?? 12 + color: button.enabled ? button.colEnabled : button.colDisabled + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + +} diff --git a/config/quickshell/modules/common/widgets/MaterialSymbol.qml b/config/quickshell/modules/common/widgets/MaterialSymbol.qml new file mode 100644 index 00000000..dbbfff00 --- /dev/null +++ b/config/quickshell/modules/common/widgets/MaterialSymbol.qml @@ -0,0 +1,30 @@ +import "root:/modules/common/" +import QtQuick +import QtQuick.Layouts + +Text { + id: root + property real iconSize: Appearance?.font.pixelSize.small ?? 16 + property real fill: 0 + renderType: Text.NativeRendering + font.hintingPreference: Font.PreferFullHinting + verticalAlignment: Text.AlignVCenter + font.family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded" + font.pixelSize: iconSize + color: Appearance.m3colors.m3onBackground + + Behavior on fill { + NumberAnimation { + duration: Appearance?.animation.elementMoveFast.duration ?? 200 + easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline + easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1] + } + } + + font.variableAxes: { + "FILL": fill, + // "wght": font.weight, + // "GRAD": 0, + "opsz": iconSize, + } +} diff --git a/config/quickshell/modules/common/widgets/PointingHandInteraction.qml b/config/quickshell/modules/common/widgets/PointingHandInteraction.qml new file mode 100644 index 00000000..cf8b065f --- /dev/null +++ b/config/quickshell/modules/common/widgets/PointingHandInteraction.qml @@ -0,0 +1,7 @@ +import QtQuick + +MouseArea { + anchors.fill: parent + onPressed: (mouse) => mouse.accepted = false + cursorShape: Qt.PointingHandCursor +} \ No newline at end of file diff --git a/config/quickshell/modules/common/widgets/RippleButton.qml b/config/quickshell/modules/common/widgets/RippleButton.qml new file mode 100644 index 00000000..cd7762b9 --- /dev/null +++ b/config/quickshell/modules/common/widgets/RippleButton.qml @@ -0,0 +1,185 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Io +import Quickshell.Widgets + +/** + * A button with ripple effect similar to in Material Design. + */ +Button { + id: root + property bool toggled + property string buttonText + property real buttonRadius: Appearance?.rounding?.small ?? 4 + property real buttonRadiusPressed: buttonRadius + property real buttonEffectiveRadius: root.down ? root.buttonRadiusPressed : root.buttonRadius + property int rippleDuration: 1200 + property bool rippleEnabled: true + property var downAction // When left clicking (down) + property var releaseAction // When left clicking (release) + property var altAction // When right clicking + property var middleClickAction // When middle clicking + + property color colBackground: ColorUtils.transparentize(Appearance?.colors.colLayer1Hover, 1) || "transparent" + property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED" + property color colBackgroundToggled: Appearance?.colors.colPrimary ?? "#65558F" + property color colBackgroundToggledHover: Appearance?.colors.colPrimaryHover ?? "#77699C" + property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2" + property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2" + + property color buttonColor: root.enabled ? (root.toggled ? + (root.hovered ? colBackgroundToggledHover : + colBackgroundToggled) : + (root.hovered ? colBackgroundHover : + colBackground)) : colBackground + property color rippleColor: root.toggled ? colRippleToggled : colRipple + + function startRipple(x, y) { + const stateY = buttonBackground.y; + rippleAnim.x = x; + rippleAnim.y = y - stateY; + + const dist = (ox,oy) => ox*ox + oy*oy + const stateEndY = stateY + buttonBackground.height + rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY))) + + rippleFadeAnim.complete(); + rippleAnim.restart(); + } + + component RippleAnim: NumberAnimation { + duration: rippleDuration + easing.type: Appearance?.animation.elementMoveEnter.type + easing.bezierCurve: Appearance?.animationCurves.standardDecel + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + onPressed: (event) => { + if(event.button === Qt.RightButton) { + if (root.altAction) root.altAction(); + return; + } + if(event.button === Qt.MiddleButton) { + if (root.middleClickAction) root.middleClickAction(); + return; + } + root.down = true + if (root.downAction) root.downAction(); + if (!root.rippleEnabled) return; + const {x,y} = event + startRipple(x, y) + } + onReleased: (event) => { + root.down = false + if (event.button != Qt.LeftButton) return; + if (root.releaseAction) root.releaseAction(); + root.click() // Because the MouseArea already consumed the event + if (!root.rippleEnabled) return; + rippleFadeAnim.restart(); + } + onCanceled: (event) => { + root.down = false + if (!root.rippleEnabled) return; + rippleFadeAnim.restart(); + } + } + + RippleAnim { + id: rippleFadeAnim + target: ripple + property: "opacity" + to: 0 + } + + SequentialAnimation { + id: rippleAnim + + property real x + property real y + property real radius + + PropertyAction { + target: ripple + property: "x" + value: rippleAnim.x + } + PropertyAction { + target: ripple + property: "y" + value: rippleAnim.y + } + PropertyAction { + target: ripple + property: "opacity" + value: 1 + } + ParallelAnimation { + RippleAnim { + target: ripple + properties: "implicitWidth,implicitHeight" + from: 0 + to: rippleAnim.radius * 2 + } + } + } + + background: Rectangle { + id: buttonBackground + radius: root.buttonEffectiveRadius + implicitHeight: 50 + + color: root.buttonColor + Behavior on color { + animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this) + } + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: buttonBackground.width + height: buttonBackground.height + radius: root.buttonEffectiveRadius + } + } + + Item { + id: ripple + width: ripple.implicitWidth + height: ripple.implicitHeight + opacity: 0 + + property real implicitWidth: 0 + property real implicitHeight: 0 + + Behavior on opacity { + animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this) + } + + RadialGradient { + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0.0; color: root.rippleColor } + GradientStop { position: 0.3; color: root.rippleColor } + GradientStop { position: 0.5; color: Qt.rgba(root.rippleColor.r, root.rippleColor.g, root.rippleColor.b, 0) } + } + } + + transform: Translate { + x: -ripple.width / 2 + y: -ripple.height / 2 + } + } + } + + contentItem: StyledText { + text: root.buttonText + } +} diff --git a/config/quickshell/modules/common/widgets/RoundCorner.qml b/config/quickshell/modules/common/widgets/RoundCorner.qml new file mode 100644 index 00000000..c9a2827a --- /dev/null +++ b/config/quickshell/modules/common/widgets/RoundCorner.qml @@ -0,0 +1,64 @@ +import QtQuick 2.9 + +Item { + id: root + + property int size: 25 + property color color: "#000000" + + onColorChanged: { + canvas.requestPaint(); + } + + property QtObject cornerEnum: QtObject { + property int topLeft: 0 + property int topRight: 1 + property int bottomLeft: 2 + property int bottomRight: 3 + } + + property int corner: cornerEnum.topLeft // Default to TopLeft + + width: size + height: size + + Canvas { + id: canvas + + anchors.fill: parent + antialiasing: true + + onPaint: { + var ctx = getContext("2d"); + var r = root.size; + + ctx.beginPath(); + switch (root.corner) { + case cornerEnum.topLeft: + ctx.arc(r, r, r, Math.PI, 3 * Math.PI / 2); + ctx.lineTo(0, 0); + break; + case cornerEnum.topRight: + ctx.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI); + ctx.lineTo(r, 0); + break; + case cornerEnum.bottomLeft: + ctx.arc(r, 0, r, Math.PI / 2, Math.PI); + ctx.lineTo(0, r); + break; + case cornerEnum.bottomRight: + ctx.arc(0, 0, r, 0, Math.PI / 2); + ctx.lineTo(r, r); + break; + } + ctx.closePath(); + ctx.fillStyle = root.color; + ctx.fill(); + } + } + + Behavior on size { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + +} diff --git a/config/quickshell/modules/common/widgets/StyledRectangularShadow.qml b/config/quickshell/modules/common/widgets/StyledRectangularShadow.qml new file mode 100644 index 00000000..6e1f2e16 --- /dev/null +++ b/config/quickshell/modules/common/widgets/StyledRectangularShadow.qml @@ -0,0 +1,13 @@ +import QtQuick +import QtQuick.Effects +import "root:/modules/common" + +RectangularShadow { + required property var target + anchors.fill: target + radius: target.radius + blur: 1.2 * Appearance.sizes.elevationMargin + spread: 1 + color: Appearance.colors.colShadow + cached: true +} diff --git a/config/quickshell/modules/common/widgets/StyledText.qml b/config/quickshell/modules/common/widgets/StyledText.qml new file mode 100644 index 00000000..6eef5785 --- /dev/null +++ b/config/quickshell/modules/common/widgets/StyledText.qml @@ -0,0 +1,14 @@ +import "root:/modules/common" +import QtQuick +import QtQuick.Layouts + +Text { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + font { + hintingPreference: Font.PreferFullHinting + family: Appearance?.font.family.main ?? "sans-serif" + pixelSize: Appearance?.font.pixelSize.small ?? 15 + } + color: Appearance?.m3colors.m3onBackground ?? "black" +} diff --git a/config/quickshell/modules/common/widgets/StyledTextArea.qml b/config/quickshell/modules/common/widgets/StyledTextArea.qml new file mode 100644 index 00000000..1ea9a349 --- /dev/null +++ b/config/quickshell/modules/common/widgets/StyledTextArea.qml @@ -0,0 +1,15 @@ +import "root:/modules/common" +import QtQuick +import QtQuick.Controls + +TextArea { + renderType: Text.NativeRendering + selectedTextColor: Appearance.m3colors.m3onSecondaryContainer + selectionColor: Appearance.m3colors.m3secondaryContainer + placeholderTextColor: Appearance.m3colors.m3outline + font { + family: Appearance?.font.family.main ?? "sans-serif" + pixelSize: Appearance?.font.pixelSize.small ?? 15 + hintingPreference: Font.PreferFullHinting + } +} diff --git a/config/quickshell/modules/common/widgets/StyledToolTip.qml b/config/quickshell/modules/common/widgets/StyledToolTip.qml new file mode 100644 index 00000000..2ca16df1 --- /dev/null +++ b/config/quickshell/modules/common/widgets/StyledToolTip.qml @@ -0,0 +1,60 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +ToolTip { + id: root + property string content + property bool extraVisibleCondition: true + property bool alternativeVisibleCondition: false + property bool internalVisibleCondition: { + const ans = (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition + return ans + } + verticalPadding: 5 + horizontalPadding: 10 + opacity: internalVisibleCondition ? 1 : 0 + visible: opacity > 0 + + Behavior on opacity { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + + background: null + + contentItem: Item { + id: contentItemBackground + implicitWidth: tooltipTextObject.width + 2 * root.horizontalPadding + implicitHeight: tooltipTextObject.height + 2 * root.verticalPadding + + Rectangle { + id: backgroundRectangle + anchors.bottom: contentItemBackground.bottom + anchors.horizontalCenter: contentItemBackground.horizontalCenter + color: Appearance?.m3colors.colTooltip ?? "#3C4043" + radius: Appearance?.rounding.verysmall ?? 7 + width: internalVisibleCondition ? (tooltipTextObject.width + 2 * padding) : 0 + height: internalVisibleCondition ? (tooltipTextObject.height + 2 * padding) : 0 + clip: true + + Behavior on width { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on height { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + + StyledText { + id: tooltipTextObject + anchors.centerIn: parent + text: content + font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14 + font.hintingPreference: Font.PreferNoHinting // Prevent shaky text + color: Appearance?.m3colors.colOnTooltip ?? "#FFFFFF" + wrapMode: Text.Wrap + } + } + } +} \ No newline at end of file diff --git a/config/quickshell/modules/overview/Overview.qml b/config/quickshell/modules/overview/Overview.qml new file mode 100644 index 00000000..ef5a49c3 --- /dev/null +++ b/config/quickshell/modules/overview/Overview.qml @@ -0,0 +1,279 @@ +import "root:/" +import "root:/services" +import "root:/modules/common" +import "root:/modules/common/widgets" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: overviewScope + property bool dontAutoCancelSearch: false + property bool searchEnabled: ConfigOptions.search.searchEnabled + + Variants { + id: overviewVariants + model: Quickshell.screens + PanelWindow { + id: root + required property var modelData + property string searchingText: "" + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) + property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id) + screen: modelData + visible: GlobalStates.overviewOpen + + WlrLayershell.namespace: "quickshell:overview" + WlrLayershell.layer: WlrLayer.Overlay + color: "transparent" + + mask: Region { + item: GlobalStates.overviewOpen ? columnLayout : null + } + HyprlandWindow.visibleMask: Region { + item: GlobalStates.overviewOpen ? columnLayout : null + } + + anchors { + top: true + left: true + right: true + bottom: true + } + + HyprlandFocusGrab { + id: grab + windows: [ root ] + property bool canBeActive: root.monitorIsFocused + active: false + onCleared: () => { + if (!active) GlobalStates.overviewOpen = false + } + } + + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (!GlobalStates.overviewOpen) { + if (overviewScope.searchEnabled && searchWidget) { + searchWidget.disableExpandAnimation() + } + overviewScope.dontAutoCancelSearch = false; + } else { + if (!overviewScope.dontAutoCancelSearch && overviewScope.searchEnabled && searchWidget) { + searchWidget.cancelSearch() + } + delayedGrabTimer.start() + } + } + } + + Timer { + id: delayedGrabTimer + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + onTriggered: { + if (!grab.canBeActive) return + grab.active = GlobalStates.overviewOpen + } + } + + implicitWidth: columnLayout.implicitWidth + implicitHeight: columnLayout.implicitHeight + + function setSearchingText(text) { + if (overviewScope.searchEnabled && searchWidget) { + searchWidget.setSearchingText(text); + } + } + + ColumnLayout { + id: columnLayout + visible: GlobalStates.overviewOpen + anchors { + horizontalCenter: parent.horizontalCenter + top: ConfigOptions.overview.position === 0 ? parent.top : undefined + verticalCenter: ConfigOptions.overview.position === 1 ? parent.verticalCenter : undefined + bottom: ConfigOptions.overview.position === 2 ? parent.bottom : undefined + } + + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape) { + GlobalStates.overviewOpen = false; + } + } + + Item { + height: 1 + width: 1 + } + + // Conditionally render SearchWidget - only exists when searchEnabled is true + SearchWidget { + id: searchWidget + Layout.alignment: Qt.AlignHCenter + visible: overviewScope.searchEnabled + height: overviewScope.searchEnabled ? implicitHeight : 0 + Layout.preferredHeight: overviewScope.searchEnabled ? implicitHeight : 0 + onSearchingTextChanged: (text) => { + root.searchingText = searchingText + } + } + + Item { + Layout.preferredHeight: overviewScope.searchEnabled ? 0 : 20 + Layout.fillWidth: true + visible: !overviewScope.searchEnabled + } + + Loader { + id: overviewLoader + active: GlobalStates.overviewOpen + sourceComponent: OverviewWidget { + panelWindow: root + // Show OverviewWidget when search is disabled OR when search text is empty + visible: !overviewScope.searchEnabled || (root.searchingText == "") + } + } + } + } + } + + IpcHandler { + target: "overview" + + function toggle() { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen + } + function close() { + GlobalStates.overviewOpen = false + } + function open() { + GlobalStates.overviewOpen = true + } + function toggleReleaseInterrupt() { + GlobalStates.superReleaseMightTrigger = false + } + // Add function to control search + function toggleSearch() { + overviewScope.searchEnabled = !overviewScope.searchEnabled + } + function enableSearch() { + overviewScope.searchEnabled = true + } + function disableSearch() { + overviewScope.searchEnabled = false + } + } + + GlobalShortcut { + name: "overviewToggle" + description: qsTr("Toggles overview on press") + + onPressed: { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen + } + } + + GlobalShortcut { + name: "overviewClose" + description: qsTr("Closes overview") + + onPressed: { + GlobalStates.overviewOpen = false + } + } + + GlobalShortcut { + name: "overviewToggleRelease" + description: qsTr("Toggles overview on release") + + onPressed: { + GlobalStates.superReleaseMightTrigger = true + } + + onReleased: { + if (!GlobalStates.superReleaseMightTrigger) { + GlobalStates.superReleaseMightTrigger = true + return + } + GlobalStates.overviewOpen = !GlobalStates.overviewOpen + } + } + + GlobalShortcut { + name: "overviewToggleReleaseInterrupt" + description: qsTr("Interrupts possibility of overview being toggled on release. ") + + qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + + qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") + + onPressed: { + GlobalStates.superReleaseMightTrigger = false + } + } + + // Only enable clipboard/emoji shortcuts when search is enabled + GlobalShortcut { + name: "overviewClipboardToggle" + description: qsTr("Toggle clipboard query on overview widget") + + onPressed: { + if (!overviewScope.searchEnabled) return; // Skip if search disabled + + if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { + GlobalStates.overviewOpen = false; + return; + } + for (let i = 0; i < overviewVariants.instances.length; i++) { + let panelWindow = overviewVariants.instances[i]; + if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + overviewScope.dontAutoCancelSearch = true; + panelWindow.setSearchingText( + ConfigOptions.search.prefix.clipboard + ); + GlobalStates.overviewOpen = true; + return + } + } + } + } + + GlobalShortcut { + name: "overviewEmojiToggle" + description: qsTr("Toggle emoji query on overview widget") + + onPressed: { + if (!overviewScope.searchEnabled) return; // Skip if search disabled + + if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { + GlobalStates.overviewOpen = false; + return; + } + for (let i = 0; i < overviewVariants.instances.length; i++) { + let panelWindow = overviewVariants.instances[i]; + if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + overviewScope.dontAutoCancelSearch = true; + panelWindow.setSearchingText( + ConfigOptions.search.prefix.emojis + ); + GlobalStates.overviewOpen = true; + return + } + } + } + } + + // Optional: Add shortcut to toggle search functionality + GlobalShortcut { + name: "overviewToggleSearch" + description: qsTr("Toggle search functionality in overview") + + onPressed: { + overviewScope.searchEnabled = !overviewScope.searchEnabled + } + } +} \ No newline at end of file diff --git a/config/quickshell/modules/overview/OverviewWidget.qml b/config/quickshell/modules/overview/OverviewWidget.qml new file mode 100644 index 00000000..73dbbc26 --- /dev/null +++ b/config/quickshell/modules/overview/OverviewWidget.qml @@ -0,0 +1,341 @@ +import "root:/" +import "root:/services/" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import Quickshell.Wayland +import Quickshell.Hyprland + +Item { + id: root + required property var panelWindow + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) + readonly property var toplevels: ToplevelManager.toplevels + readonly property int workspacesShown: ConfigOptions.overview.numOfRows * ConfigOptions.overview.numOfCols + readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown) + property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id) + property var windows: HyprlandData.windowList + property var windowByAddress: HyprlandData.windowByAddress + property var windowAddresses: HyprlandData.addresses + property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id) + property real scale: ConfigOptions.overview.scale + property color activeBorderColor: Appearance.m3colors.m3secondary + + property real workspaceImplicitWidth: Math.max(100, (monitorData?.transform % 2 === 1) ? + ((monitor.height - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) : + ((monitor.width - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale)) + property real workspaceImplicitHeight: Math.max(60, (monitorData?.transform % 2 === 1) ? + ((monitor.width - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) : + ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale)) + + property real workspaceNumberMargin: 80 + property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale + property int workspaceZ: 0 + property int windowZ: 1 + property int windowDraggingZ: 99999 + property real workspaceSpacing: 5 + + property int draggingFromWorkspace: -1 + property int draggingTargetWorkspace: -1 + + implicitWidth: overviewBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 + implicitHeight: overviewBackground.implicitHeight + Appearance.sizes.elevationMargin * 2 + + property Component windowComponent: OverviewWindow {} + property list windowWidgets: [] + + // Shared wallpaper image - loaded once and reused + Image { + id: sharedWallpaper + source: Appearance.background_image || "" + visible: false // Hidden as it's only used as a source + cache: true + asynchronous: true + smooth: true + opacity: Appearance.workpaceTransparency // Adds slight transparency (0.0 = fully transparent, 1.0 = fully opaque) + } + + StyledRectangularShadow { + target: overviewBackground + } + Rectangle { // Background + id: overviewBackground + property real padding: 10 + anchors.fill: parent + anchors.margins: Appearance.sizes.elevationMargin + border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.2) + border.width : 2 + + implicitWidth: workspaceColumnLayout.implicitWidth + padding * 2 + implicitHeight: workspaceColumnLayout.implicitHeight + padding * 2 + radius: Appearance.rounding.screenRounding * root.scale + padding + color: Appearance.colors.colLayer0 + + + ColumnLayout { // Workspaces + id: workspaceColumnLayout + + z: root.workspaceZ + anchors.centerIn: parent + spacing: workspaceSpacing + Repeater { + model: ConfigOptions.overview.numOfRows + delegate: RowLayout { + id: row + property int rowIndex: index + spacing: workspaceSpacing + + Repeater { // Workspace repeater + model: ConfigOptions.overview.numOfCols + Rectangle { // Workspace + id: workspace + property int colIndex: index + property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * ConfigOptions.overview.numOfCols + colIndex + 1 + property color defaultWorkspaceColor: Appearance.colors.colLayer1 + property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) + property color hoveredBorderColor: Appearance.colors.colLayer2Hover + property bool hoveredWhileDragging: false + readonly property int padding: ConfigOptions.overview.windowPadding + + Layout.preferredWidth: root.workspaceImplicitWidth + Layout.preferredHeight: root.workspaceImplicitHeight + Layout.minimumWidth: 100 + Layout.minimumHeight: 60 + + width: root.workspaceImplicitWidth + height: root.workspaceImplicitHeight + color: "transparent" + radius: Appearance.rounding.screenRounding * root.scale + clip: true + opacity: Appearance.workpaceTransparency // Adds slight transparency (0.0 = fully transparent, 1.0 = fully opaque) + + + // Efficient wallpaper using ShaderEffectSource + Rectangle { + id: wallpaperContainer + anchors.fill: parent + anchors.margins: 2 // Leave space for border + radius: workspace.radius - 2 + color: workspace.defaultWorkspaceColor // Fallback color + clip: true + + ShaderEffectSource { + id: wallpaperSource + anchors.fill: parent + sourceItem: sharedWallpaper + visible: sharedWallpaper.status === Image.Ready + smooth: true + + // Scale to fill while preserving aspect ratio + transform: Scale { + property real aspectRatio: sharedWallpaper.implicitWidth / Math.max(1, sharedWallpaper.implicitHeight) + property real containerRatio: wallpaperContainer.width / Math.max(1, wallpaperContainer.height) + + xScale: aspectRatio > containerRatio ? + wallpaperContainer.height * aspectRatio / wallpaperContainer.width : 1 + yScale: aspectRatio > containerRatio ? + 1 : wallpaperContainer.width / (wallpaperContainer.height * aspectRatio) + + origin.x: wallpaperContainer.width / 2 + origin.y: wallpaperContainer.height / 2 + } + } + + // Fallback when image fails to load or isn't ready + Rectangle { + anchors.fill: parent + color: workspace.defaultWorkspaceColor + visible: sharedWallpaper.status !== Image.Ready + } + + // Optional: Add overlay for better text readability and hover effects + Rectangle { + anchors.fill: parent + color: hoveredWhileDragging ? hoveredWorkspaceColor : "black" + opacity: hoveredWhileDragging ? 0.3 : 0.1 + } + } + + // Border overlay - on top of wallpaper + Rectangle { + anchors.fill: parent + color: "transparent" + radius: parent.radius + border.width: 1 + border.color: hoveredWhileDragging ? hoveredBorderColor : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.6) + z: 10 // Ensure it's on top + } + + StyledText { + // Position in top-left corner with padding + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: 12 // Padding from top edge + anchors.leftMargin: 12 // Padding from left edge + + text: workspaceValue + font.pixelSize: root.workspaceNumberSize * root.scale + font.weight: Font.DemiBold + color: ColorUtils.transparentize(Appearance.colors.colOnLayer1, 0.8) + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignTop + z: 15 // Above border + } + + MouseArea { + id: workspaceArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton + z: 20 // Above all visual elements + onClicked: { + if (root.draggingTargetWorkspace === -1) { + GlobalStates.overviewOpen = false + Hyprland.dispatch(`workspace ${workspaceValue}`) + } + } + } + + DropArea { + anchors.fill: parent + z: 20 // Same level as MouseArea + onEntered: { + root.draggingTargetWorkspace = workspaceValue + if (root.draggingFromWorkspace == root.draggingTargetWorkspace) return; + hoveredWhileDragging = true + } + onExited: { + hoveredWhileDragging = false + if (root.draggingTargetWorkspace == workspaceValue) root.draggingTargetWorkspace = -1 + } + } + + } + } + } + } + } + + Item { // Windows & focused workspace indicator + id: windowSpace + anchors.centerIn: parent + implicitWidth: workspaceColumnLayout.implicitWidth + implicitHeight: workspaceColumnLayout.implicitHeight + + Repeater { // Window repeater + model: ScriptModel { + values: windowAddresses.filter((address) => { + var win = windowByAddress[address] + return (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) + }) + } + delegate: OverviewWindow { + id: window + windowData: windowByAddress[modelData] + monitorData: root.monitorData + scale: root.scale + availableWorkspaceWidth: root.workspaceImplicitWidth + availableWorkspaceHeight: root.workspaceImplicitHeight + + property bool atInitPosition: (initX == x && initY == y) + restrictToWorkspace: Drag.active || atInitPosition + + property int workspaceColIndex: (windowData?.workspace.id - 1) % ConfigOptions.overview.numOfCols + property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / ConfigOptions.overview.numOfCols) + xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex + yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex + + Timer { + id: updateWindowPosition + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + running: false + onTriggered: { + window.x = Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset + window.y = Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset + } + } + + z: atInitPosition ? root.windowZ : root.windowDraggingZ + Drag.hotSpot.x: targetWindowWidth / 2 + Drag.hotSpot.y: targetWindowHeight / 2 + MouseArea { + id: dragArea + anchors.fill: parent + hoverEnabled: true + onEntered: hovered = true + onExited: hovered = false + acceptedButtons: Qt.LeftButton | Qt.MiddleButton + drag.target: parent + onPressed: { + root.draggingFromWorkspace = windowData?.workspace.id + window.pressed = true + window.Drag.active = true + window.Drag.source = window + } + onReleased: { + const targetWorkspace = root.draggingTargetWorkspace + window.pressed = false + window.Drag.active = false + root.draggingFromWorkspace = -1 + if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { + Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`) + updateWindowPosition.restart() + } + else { + window.x = window.initX + window.y = window.initY + } + } + onClicked: (event) => { + if (!windowData) return; + + if (event.button === Qt.LeftButton) { + GlobalStates.overviewOpen = false + Hyprland.dispatch(`focuswindow address:${windowData.address}`) + event.accepted = true + } else if (event.button === Qt.MiddleButton) { + Hyprland.dispatch(`closewindow address:${windowData.address}`) + event.accepted = true + } + } + + StyledToolTip { + extraVisibleCondition: false + alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active + content: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n` + } + } + } + } + + Rectangle { // Focused workspace indicator + id: focusedWorkspaceIndicator + property int activeWorkspaceInGroup: monitor.activeWorkspace?.id - (root.workspaceGroup * root.workspacesShown) + property int activeWorkspaceRowIndex: Math.floor((activeWorkspaceInGroup - 1) / ConfigOptions.overview.numOfCols) + property int activeWorkspaceColIndex: (activeWorkspaceInGroup - 1) % ConfigOptions.overview.numOfCols + x: (root.workspaceImplicitWidth + workspaceSpacing) * activeWorkspaceColIndex + y: (root.workspaceImplicitHeight + workspaceSpacing) * activeWorkspaceRowIndex + z: root.windowZ + width: Math.max(100, root.workspaceImplicitWidth) + height: Math.max(60, root.workspaceImplicitHeight) + color: "transparent" + radius: Appearance.rounding.screenRounding * root.scale + border.width: 2 + border.color: root.activeBorderColor + visible: width > 0 && height > 0 && activeWorkspaceInGroup > 0 + Behavior on x { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on y { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + } + } +} \ No newline at end of file diff --git a/config/quickshell/modules/overview/OverviewWindow.qml b/config/quickshell/modules/overview/OverviewWindow.qml new file mode 100644 index 00000000..273eff7e --- /dev/null +++ b/config/quickshell/modules/overview/OverviewWindow.qml @@ -0,0 +1,94 @@ +import "root:/services/" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets +import Quickshell.Io +import Quickshell.Hyprland + +Rectangle { // Window + id: root + property var windowData + property var monitorData + property var scale + property var availableWorkspaceWidth + property var availableWorkspaceHeight + property bool restrictToWorkspace: true + property real initX: Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset + property real initY: Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset + property real xOffset: 0 + property real yOffset: 0 + + property var targetWindowWidth: windowData?.size[0] * scale + property var targetWindowHeight: windowData?.size[1] * scale + property bool hovered: false + property bool pressed: false + + property var iconToWindowRatio: 0.35 + property var xwaylandIndicatorToIconRatio: 0.35 + property var iconToWindowRatioCompact: 0.6 + property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing") + property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth + + property bool indicateXWayland: (ConfigOptions.overview.showXwaylandIndicator && windowData?.xwayland) ?? false + + x: initX + y: initY + width: Math.min(windowData?.size[0] * root.scale, (restrictToWorkspace ? windowData?.size[0] : availableWorkspaceWidth - x + xOffset)) + height: Math.min(windowData?.size[1] * root.scale, (restrictToWorkspace ? windowData?.size[1] : availableWorkspaceHeight - y + yOffset)) + + radius: Appearance.rounding.windowRounding * root.scale + color: pressed ? Appearance.colors.colLayer2Active : hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2 + // border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.9) + border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.4) + border.pixelAligned : false + border.width : 2 + + Behavior on x { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + Behavior on y { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + Behavior on width { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + Behavior on height { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + + ColumnLayout { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.font.pixelSize.smaller * 0.5 + + IconImage { + id: windowIcon + Layout.alignment: Qt.AlignHCenter + source: root.iconPath + implicitSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) + + Behavior on implicitSize { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + } + + StyledText { + Layout.leftMargin: 10 + Layout.rightMargin: 10 + visible: !compactMode + Layout.fillWidth: true + Layout.fillHeight: true + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Appearance.font.pixelSize.smaller + font.italic: indicateXWayland ? true : false + elide: Text.ElideRight + text: windowData?.title ?? "" + } + } +} \ No newline at end of file diff --git a/config/quickshell/modules/overview/SearchItem.qml b/config/quickshell/modules/overview/SearchItem.qml new file mode 100644 index 00000000..1363b88d --- /dev/null +++ b/config/quickshell/modules/overview/SearchItem.qml @@ -0,0 +1,220 @@ +// pragma NativeMethodBehavior: AcceptThisObject +import "root:/" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils +import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/fuzzysort.js" as Fuzzy +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import Quickshell.Hyprland + +RippleButton { + id: root + property var entry + property string query + property bool entryShown: entry?.shown ?? true + property string itemType: entry?.type + property string itemName: entry?.name + property string itemIcon: entry?.icon ?? "" + property var itemExecute: entry?.execute + property string fontType: entry?.fontType ?? "main" + property string itemClickActionName: entry?.clickActionName + property string bigText: entry?.bigText ?? "" + property string materialSymbol: entry?.materialSymbol ?? "" + property string cliphistRawString: entry?.cliphistRawString ?? "" + + property string highlightPrefix: `` + property string highlightSuffix: `` + function highlightContent(content, query) { + if (!query || query.length === 0 || content == query || fontType === "monospace") + return StringUtils.escapeHtml(content); + + let contentLower = content.toLowerCase(); + let queryLower = query.toLowerCase(); + + let result = ""; + let lastIndex = 0; + let qIndex = 0; + + for (let i = 0; i < content.length && qIndex < query.length; i++) { + if (contentLower[i] === queryLower[qIndex]) { + // Add non-highlighted part (escaped) + if (i > lastIndex) + result += StringUtils.escapeHtml(content.slice(lastIndex, i)); + // Add highlighted character (escaped) + result += root.highlightPrefix + StringUtils.escapeHtml(content[i]) + root.highlightSuffix; + lastIndex = i + 1; + qIndex++; + } + } + // Add the rest of the string (escaped) + if (lastIndex < content.length) + result += StringUtils.escapeHtml(content.slice(lastIndex)); + + return result; + } + property string displayContent: highlightContent(root.itemName, root.query) + + property list urls: { + if (!root.itemName) return []; + // Regular expression to match URLs + const urlRegex = /https?:\/\/[^\s<>"{}|\\^`[\]]+/gi; + const matches = root.itemName?.match(urlRegex) + ?.filter(url => !url.includes("…")) // Elided = invalid + return matches ? matches : []; + } + + visible: root.entryShown + property int horizontalMargin: 10 + property int buttonHorizontalPadding: 10 + property int buttonVerticalPadding: 5 + property bool keyboardDown: false + + implicitHeight: rowLayout.implicitHeight + root.buttonVerticalPadding * 2 + implicitWidth: rowLayout.implicitWidth + root.buttonHorizontalPadding * 2 + buttonRadius: Appearance.rounding.normal + colBackground: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active : + ((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover : + ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) + colBackgroundHover: Appearance.colors.colLayer1Hover + colRipple: Appearance.colors.colLayer1Active + + background { + anchors.fill: root + anchors.leftMargin: root.horizontalMargin + anchors.rightMargin: root.horizontalMargin + } + + PointingHandInteraction {} + onClicked: { + root.itemExecute() + Hyprland.dispatch("global quickshell:overviewClose") + } + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + root.keyboardDown = true + root.clicked() + event.accepted = true; + } + } + Keys.onReleased: (event) => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + root.keyboardDown = false + event.accepted = true; + } + } + + RowLayout { + id: rowLayout + spacing: iconLoader.sourceComponent === null ? 0 : 10 + anchors.fill: parent + anchors.leftMargin: root.horizontalMargin + root.buttonHorizontalPadding + anchors.rightMargin: root.horizontalMargin + root.buttonHorizontalPadding + + // Icon + Loader { + id: iconLoader + active: true + sourceComponent: root.materialSymbol !== "" ? materialSymbolComponent : + root.bigText ? bigTextComponent : + root.itemIcon !== "" ? iconImageComponent : + null + } + + Component { + id: iconImageComponent + IconImage { + source: Quickshell.iconPath(root.itemIcon, "image-missing") + width: 35 + height: 35 + } + } + + Component { + id: materialSymbolComponent + MaterialSymbol { + text: root.materialSymbol + iconSize: 30 + color: Appearance.m3colors.m3onSurface + } + } + + Component { + id: bigTextComponent + StyledText { + text: root.bigText + font.pixelSize: Appearance.font.pixelSize.larger + color: Appearance.m3colors.m3onSurface + } + } + + // Main text + ColumnLayout { + id: contentColumn + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 0 + StyledText { + font.pixelSize: Appearance.font.pixelSize.smaller + color: Appearance.colors.colSubtext + visible: root.itemType && root.itemType != qsTr("App") + text: root.itemType + } + RowLayout { + Loader { // Checkmark for copied clipboard entry + visible: itemName == Quickshell.clipboardText && root.cliphistRawString + active: itemName == Quickshell.clipboardText && root.cliphistRawString + sourceComponent: Rectangle { + implicitWidth: activeText.implicitHeight + implicitHeight: activeText.implicitHeight + radius: Appearance.rounding.full + color: Appearance.colors.colPrimary + MaterialSymbol { + id: activeText + anchors.centerIn: parent + text: "check" + font.pixelSize: Appearance.font.pixelSize.normal + color: Appearance.m3colors.m3onPrimary + } + } + } + StyledText { // Item name/content + Layout.fillWidth: true + id: nameText + textFormat: Text.StyledText // RichText also works, but StyledText ensures elide work + font.pixelSize: Appearance.font.pixelSize.small + font.family: Appearance.font.family[root.fontType] + color: Appearance.m3colors.m3onSurface + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + text: `${root.displayContent}` + } + } + Loader { // Clipboard image preview + active: root.cliphistRawString && /^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(root.cliphistRawString) + sourceComponent: CliphistImage { + Layout.fillWidth: true + entry: root.cliphistRawString + maxWidth: contentColumn.width + maxHeight: 140 + } + } + } + + // Action text + StyledText { + Layout.fillWidth: false + visible: (root.hovered || root.focus) + id: clickAction + font.pixelSize: Appearance.font.pixelSize.normal + color: Appearance.colors.colSubtext + horizontalAlignment: Text.AlignRight + text: root.itemClickActionName + } + } +} diff --git a/config/quickshell/modules/overview/SearchWidget.qml b/config/quickshell/modules/overview/SearchWidget.qml new file mode 100644 index 00000000..fed710ec --- /dev/null +++ b/config/quickshell/modules/overview/SearchWidget.qml @@ -0,0 +1,425 @@ +import "root:/" +import "root:/services/" +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/modules/common/functions/string_utils.js" as StringUtils +import Qt5Compat.GraphicalEffects +import Qt.labs.platform +import QtQuick +import QtQuick.Controls +import QtQuick.Effects +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Hyprland + +Item { // Wrapper + id: root + readonly property string xdgConfigHome: Directories.config + property string searchingText: "" + property bool showResults: searchingText != "" + property real searchBarHeight: searchBar.height + Appearance.sizes.elevationMargin * 2 + implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 + implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2 + + property string mathResult: "" + + function disableExpandAnimation() { + searchWidthBehavior.enabled = false; + } + + function cancelSearch() { + searchInput.selectAll() + root.searchingText = "" + searchWidthBehavior.enabled = true; + } + + function setSearchingText(text) { + searchInput.text = text; + root.searchingText = text; + } + + property var searchActions: [ + { + action: "img", + execute: () => { + executor.executeCommand(Directories.wallpaperSwitchScriptPath) + } + }, + { + action: "dark", + execute: () => { + executor.executeCommand(`${Directories.wallpaperSwitchScriptPath} --mode dark --noswitch`) + } + }, + { + action: "light", + execute: () => { + executor.executeCommand(`${Directories.wallpaperSwitchScriptPath} --mode light --noswitch`) + } + }, + { + action: "accentcolor", + execute: (args) => { + executor.executeCommand( + `${Directories.wallpaperSwitchScriptPath} --noswitch --color ${args != '' ? ("'"+args+"'") : ""}` + ) + } + }, + { + action: "todo", + execute: (args) => { + Todo.addTask(args) + } + }, + ] + + function focusFirstItemIfNeeded() { + if (searchInput.focus) appResults.currentIndex = 0; // Focus the first item + } + + Timer { + id: nonAppResultsTimer + interval: ConfigOptions.search.nonAppResultDelay + onTriggered: { + mathProcess.calculateExpression(root.searchingText); + } + } + + Process { + id: mathProcess + property list baseCommand: ["qalc", "-t"] + function calculateExpression(expression) { + // mathProcess.running = false + mathProcess.command = baseCommand.concat(expression) + mathProcess.running = true + } + stdout: SplitParser { + onRead: data => { + root.mathResult = data + root.focusFirstItemIfNeeded() + } + } + } + + Process { + id: executor + property list baseCommand: ["bash", "-c"] + function executeCommand(command) { + executor.command = baseCommand.concat( + `${command} || ${ConfigOptions.apps.terminal} fish -C 'echo "${qsTr("Searching for package with that command")}..." && pacman -F ${command}'` + ) + executor.startDetached() + } + } + + Keys.onPressed: (event) => { + // Prevent Esc and Backspace from registering + if (event.key === Qt.Key_Escape) return; + + // Handle Backspace: focus and delete character if not focused + if (event.key === Qt.Key_Backspace) { + if (!searchInput.activeFocus) { + searchInput.forceActiveFocus(); + if (event.modifiers & Qt.ControlModifier) { + // Delete word before cursor + let text = searchInput.text; + let pos = searchInput.cursorPosition; + if (pos > 0) { + // Find the start of the previous word + let left = text.slice(0, pos); + let match = left.match(/(\s*\S+)\s*$/); + let deleteLen = match ? match[0].length : 1; + searchInput.text = text.slice(0, pos - deleteLen) + text.slice(pos); + searchInput.cursorPosition = pos - deleteLen; + } + } else { + // Delete character before cursor if any + if (searchInput.cursorPosition > 0) { + searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition - 1) + + searchInput.text.slice(searchInput.cursorPosition); + searchInput.cursorPosition -= 1; + } + } + // Always move cursor to end after programmatic edit + searchInput.cursorPosition = searchInput.text.length; + event.accepted = true; + } + // If already focused, let TextField handle it + return; + } + + // Only handle visible printable characters (ignore control chars, arrows, etc.) + if ( + event.text && + event.text.length === 1 && + event.key !== Qt.Key_Enter && + event.key !== Qt.Key_Return && + event.text.charCodeAt(0) >= 0x20 // ignore control chars like Backspace, Tab, etc. + ) { + if (!searchInput.activeFocus) { + searchInput.forceActiveFocus(); + // Insert the character at the cursor position + searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition) + + event.text + + searchInput.text.slice(searchInput.cursorPosition); + searchInput.cursorPosition += 1; + event.accepted = true; + } + } + } + + StyledRectangularShadow { + target: searchWidgetContent + } + Rectangle { // Background + id: searchWidgetContent + anchors.centerIn: parent + implicitWidth: columnLayout.implicitWidth + implicitHeight: columnLayout.implicitHeight + radius: Appearance.rounding.large + color: Appearance.colors.colLayer0 + + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + spacing: 0 + + // clip: true + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: searchWidgetContent.width + height: searchWidgetContent.width + radius: searchWidgetContent.radius + } + } + + RowLayout { + id: searchBar + spacing: 5 + MaterialSymbol { + id: searchIcon + Layout.leftMargin: 15 + iconSize: Appearance.font.pixelSize.huge + color: Appearance.m3colors.m3onSurface + text: root.searchingText.startsWith(ConfigOptions.search.prefix.clipboard) ? 'content_paste_search' : '' + } + TextField { // Search box + id: searchInput + + focus: GlobalStates.overviewOpen + Layout.rightMargin: 15 + padding: 15 + renderType: Text.NativeRendering + font { + family: Appearance?.font.family.main ?? "sans-serif" + pixelSize: Appearance?.font.pixelSize.small ?? 15 + hintingPreference: Font.PreferFullHinting + } + color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant + selectedTextColor: Appearance.m3colors.m3onSecondaryContainer + selectionColor: Appearance.m3colors.m3secondaryContainer + placeholderText: qsTr("Search, calculate or run") + placeholderTextColor: Appearance.m3colors.m3outline + implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth + + Behavior on implicitWidth { + id: searchWidthBehavior + enabled: false + NumberAnimation { + duration: 300 + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } + } + + onTextChanged: root.searchingText = text + + onAccepted: { + if (appResults.count > 0) { + // Get the first visible delegate and trigger its click + let firstItem = appResults.itemAtIndex(0); + if (firstItem && firstItem.clicked) { + firstItem.clicked(); + } + } + } + + background: null + + cursorDelegate: Rectangle { + width: 1 + color: searchInput.activeFocus ? Appearance.colors.colPrimary : "transparent" + radius: 1 + } + } + } + + Rectangle { // Separator + visible: root.showResults + Layout.fillWidth: true + height: 1 + color: Appearance.m3colors.m3outlineVariant + } + + ListView { // App results + id: appResults + visible: root.showResults + Layout.fillWidth: true + implicitHeight: Math.min(600, appResults.contentHeight + topMargin + bottomMargin) + clip: true + topMargin: 10 + bottomMargin: 10 + spacing: 2 + KeyNavigation.up: searchBar + highlightMoveDuration : 100 + + onFocusChanged: { + if(focus) appResults.currentIndex = 1; + } + + Connections { + target: root + function onSearchingTextChanged() { + if (appResults.count > 0) + appResults.currentIndex = 0; + } + } + + model: ScriptModel { + id: model + values: { // Search results are handled here + ////////////////// Skip? ////////////////// + if(root.searchingText == "") return []; + + ///////////// Special cases /////////////// + if (root.searchingText.startsWith(ConfigOptions.search.prefix.clipboard)) { // Clipboard + const searchString = root.searchingText.slice(ConfigOptions.search.prefix.clipboard.length); + return Cliphist.fuzzyQuery(searchString).map(entry => { + return { + cliphistRawString: entry, + name: entry.replace(/^\s*\S+\s+/, ""), + clickActionName: "", + type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`, + execute: () => { + Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`); + } + }; + }).filter(Boolean); + } + if (root.searchingText.startsWith(ConfigOptions.search.prefix.emojis)) { // Clipboard + const searchString = root.searchingText.slice(ConfigOptions.search.prefix.emojis.length); + return Emojis.fuzzyQuery(searchString).map(entry => { + return { + cliphistRawString: entry, + bigText: entry.match(/^\s*(\S+)/)?.[1] || "", + name: entry.replace(/^\s*\S+\s+/, ""), + clickActionName: "", + type: "Emoji", + execute: () => { + Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(entry.match(/^\s*(\S+)/)?.[1])}'`); + } + }; + }).filter(Boolean); + } + + + ////////////////// Init /////////////////// + nonAppResultsTimer.restart(); + const mathResultObject = { + name: root.mathResult, + clickActionName: qsTr("Copy"), + type: qsTr("Math result"), + fontType: "monospace", + materialSymbol: 'calculate', + execute: () => { + Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(root.mathResult)}'`) + } + } + const commandResultObject = { + name: searchingText.replace("file://", ""), + clickActionName: qsTr("Run"), + type: qsTr("Run command"), + fontType: "monospace", + materialSymbol: 'terminal', + execute: () => { + executor.executeCommand(searchingText.startsWith('sudo') ? `${ConfigOptions.apps.terminal} fish -C '${root.searchingText.replace("file://", "")}'` : root.searchingText.replace("file://", "")); + } + } + const launcherActionObjects = root.searchActions + .map(action => { + const actionString = `${ConfigOptions.search.prefix.action}${action.action}`; + if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) { + return { + name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString, + clickActionName: qsTr("Run"), + type: qsTr("Action"), + materialSymbol: 'settings_suggest', + execute: () => { + action.execute(root.searchingText.split(" ").slice(1).join(" ")) + }, + }; + } + return null; + }) + .filter(Boolean); + + let result = []; + + //////////////// Apps ////////////////// + result = result.concat( + AppSearch.fuzzyQuery(root.searchingText) + .map((entry) => { + entry.clickActionName = qsTr("Launch"); + entry.type = qsTr("App"); + return entry; + }) + ); + + ////////// Launcher actions //////////// + result = result.concat(launcherActionObjects); + + /////////// Math result & command ////////// + const startsWithNumber = /^\d/.test(root.searchingText); + if (startsWithNumber) { + result.push(mathResultObject); + result.push(commandResultObject); + } else { + result.push(commandResultObject); + result.push(mathResultObject); + } + + ///////////////// Web search //////////////// + result.push({ + name: root.searchingText, + clickActionName: qsTr("Search"), + type: qsTr("Search the web"), + materialSymbol: 'travel_explore', + execute: () => { + let url = ConfigOptions.search.engineBaseUrl + root.searchingText + for (let site of ConfigOptions.search.excludedSites) { + url += ` -site:${site}`; + } + Qt.openUrlExternally(url); + } + }); + + return result; + } + } + + delegate: SearchItem { // The selectable item for each search result + required property var modelData + anchors.left: parent?.left + anchors.right: parent?.right + entry: modelData + query: root.searchingText.startsWith(ConfigOptions.search.prefix.clipboard) ? + root.searchingText.slice(ConfigOptions.search.prefix.clipboard.length) : + root.searchingText; + } + } + + } + } +} \ No newline at end of file diff --git a/config/quickshell/services/AppSearch.qml b/config/quickshell/services/AppSearch.qml new file mode 100644 index 00000000..876df183 --- /dev/null +++ b/config/quickshell/services/AppSearch.qml @@ -0,0 +1,116 @@ +pragma Singleton + +import "root:/modules/common" +import "root:/modules/common/functions/fuzzysort.js" as Fuzzy +import "root:/modules/common/functions/levendist.js" as Levendist +import Quickshell +import Quickshell.Io + +/** + * - Eases fuzzy searching for applications by name + * - Guesses icon name for window class name + */ +Singleton { + id: root + property bool sloppySearch: ConfigOptions?.search.sloppy ?? false + property real scoreThreshold: 0.2 + property var substitutions: ({ + "code-url-handler": "visual-studio-code", + "Code": "visual-studio-code", + "gnome-tweaks": "org.gnome.tweaks", + "pavucontrol-qt": "pavucontrol", + "wps": "wps-office2019-kprometheus", + "wpsoffice": "wps-office2019-kprometheus", + "footclient": "foot", + "zen": "zen-browser", + }) + property var regexSubstitutions: [ + { + "regex": /^steam_app_(\\d+)$/, + "replace": "steam_icon_$1" + }, + { + "regex": /Minecraft.*/, + "replace": "minecraft" + }, + { + "regex": /.*polkit.*/, + "replace": "system-lock-screen" + }, + { + "regex": /gcr.prompter/, + "replace": "system-lock-screen" + } + ] + + readonly property list list: Array.from(DesktopEntries.applications.values) + .sort((a, b) => a.name.localeCompare(b.name)) + + readonly property var preppedNames: list.map(a => ({ + name: Fuzzy.prepare(`${a.name} `), + entry: a + })) + + function fuzzyQuery(search: string): var { // Idk why list doesn't work + if (root.sloppySearch) { + const results = list.map(obj => ({ + entry: obj, + score: Levendist.computeScore(obj.name.toLowerCase(), search.toLowerCase()) + })).filter(item => item.score > root.scoreThreshold) + .sort((a, b) => b.score - a.score) + return results + .map(item => item.entry) + } + + return Fuzzy.go(search, preppedNames, { + all: true, + key: "name" + }).map(r => { + return r.obj.entry + }); + } + + function iconExists(iconName) { + return (Quickshell.iconPath(iconName, true).length > 0) + && !iconName.includes("image-missing"); + } + + function guessIcon(str) { + if (!str || str.length == 0) return "image-missing"; + + // Normal substitutions + if (substitutions[str]) + return substitutions[str]; + + // Regex substitutions + for (let i = 0; i < regexSubstitutions.length; i++) { + const substitution = regexSubstitutions[i]; + const replacedName = str.replace( + substitution.regex, + substitution.replace, + ); + if (replacedName != str) return replacedName; + } + + // If it gets detected normally, no need to guess + if (iconExists(str)) return str; + + let guessStr = str; + // Guess: Take only app name of reverse domain name notation + guessStr = str.split('.').slice(-1)[0].toLowerCase(); + if (iconExists(guessStr)) return guessStr; + // Guess: normalize to kebab case + guessStr = str.toLowerCase().replace(/\s+/g, "-"); + if (iconExists(guessStr)) return guessStr; + // Guess: First fuzze desktop entry match + const searchResults = root.fuzzyQuery(str); + if (searchResults.length > 0) { + const firstEntry = searchResults[0]; + guessStr = firstEntry.icon + if (iconExists(guessStr)) return guessStr; + } + + // Give up + return str; + } +} diff --git a/config/quickshell/services/ConfigLoader.qml b/config/quickshell/services/ConfigLoader.qml new file mode 100644 index 00000000..051162d1 --- /dev/null +++ b/config/quickshell/services/ConfigLoader.qml @@ -0,0 +1,116 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import "root:/modules/common" +import "root:/modules/common/functions/file_utils.js" as FileUtils +import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/object_utils.js" as ObjectUtils +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Hyprland +import Qt.labs.platform + +/** + * Loads and manages the shell configuration file. + * The config file is by default at XDG_CONFIG_HOME/quickshell/config.json. + * Automatically reloaded when the file changes, but does not provide a way to save changes. + */ +Singleton { + id: root + property string filePath: Directories.shellConfigPath + property bool firstLoad: true + + function loadConfig() { + configFileView.reload() + } + + function applyConfig(fileContent) { + try { + const json = JSON.parse(fileContent); + + ObjectUtils.applyToQtObject(ConfigOptions, json); + if (root.firstLoad) { + root.firstLoad = false; + } else { + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`) + } + } catch (e) { + console.error("[ConfigLoader] Error reading file:", e); + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) + return; + + } + } + + function setLiveConfigValue(nestedKey, value) { + let keys = nestedKey.split("."); + let obj = ConfigOptions; + let parents = [obj]; + + // Traverse and collect parent objects + for (let i = 0; i < keys.length - 1; ++i) { + if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") { + obj[keys[i]] = {}; + } + obj = obj[keys[i]]; + parents.push(obj); + } + + // Convert value to correct type using JSON.parse when safe + let convertedValue = value; + if (typeof value === "string") { + let trimmed = value.trim(); + if (trimmed === "true" || trimmed === "false" || !isNaN(Number(trimmed))) { + try { + convertedValue = JSON.parse(trimmed); + } catch (e) { + convertedValue = value; + } + } + } + + console.log(parents.join(".")); + console.log(`[ConfigLoader] Setting live config value: ${nestedKey} = ${convertedValue}`); + obj[keys[keys.length - 1]] = convertedValue; + } + + function saveConfig() { + const plainConfig = ObjectUtils.toPlainObject(ConfigOptions) + Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(JSON.stringify(plainConfig, null, 2))}' > '${root.filePath}'`) + } + + Timer { + id: delayedFileRead + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + running: false + onTriggered: { + root.applyConfig(configFileView.text()) + } + } + + FileView { + id: configFileView + path: Qt.resolvedUrl(root.filePath) + watchChanges: true + onFileChanged: { + console.log("[ConfigLoader] File changed, reloading...") + this.reload() + delayedFileRead.start() + } + onLoadedChanged: { + const fileContent = configFileView.text() + root.applyConfig(fileContent) + } + onLoadFailed: (error) => { + if(error == FileViewError.FileNotFound) { + console.log("[ConfigLoader] File not found, creating new file.") + root.saveConfig() + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration created")}" "${root.filePath}"`) + } else { + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) + } + } + } +} diff --git a/config/quickshell/services/HyprlandData.qml b/config/quickshell/services/HyprlandData.qml new file mode 100644 index 00000000..2b88ad9c --- /dev/null +++ b/config/quickshell/services/HyprlandData.qml @@ -0,0 +1,69 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +/** + * Provides access to some Hyprland data not available in Quickshell.Hyprland. + */ +Singleton { + id: root + property var windowList: [] + property var addresses: [] + property var windowByAddress: ({}) + property var monitors: [] + + function updateWindowList() { + getClients.running = true + getMonitors.running = true + } + + Component.onCompleted: { + updateWindowList() + } + + Connections { + target: Hyprland + + function onRawEvent(event) { + // Filter out redundant old v1 events for the same thing + if(event.name in [ + "activewindow", "focusedmon", "monitoradded", + "createworkspace", "destroyworkspace", "moveworkspace", + "activespecial", "movewindow", "windowtitle" + ]) return ; + updateWindowList() + } + } + + Process { + id: getClients + command: ["bash", "-c", "hyprctl clients -j | jq -c"] + stdout: SplitParser { + onRead: (data) => { + root.windowList = JSON.parse(data) + let tempWinByAddress = {} + for (var i = 0; i < root.windowList.length; ++i) { + var win = root.windowList[i] + tempWinByAddress[win.address] = win + } + root.windowByAddress = tempWinByAddress + root.addresses = root.windowList.map((win) => win.address) + } + } + } + Process { + id: getMonitors + command: ["bash", "-c", "hyprctl monitors -j | jq -c"] + stdout: SplitParser { + onRead: (data) => { + root.monitors = JSON.parse(data) + } + } + } +} + diff --git a/config/quickshell/services/HyprlandKeybinds.qml b/config/quickshell/services/HyprlandKeybinds.qml new file mode 100644 index 00000000..189ba76d --- /dev/null +++ b/config/quickshell/services/HyprlandKeybinds.qml @@ -0,0 +1,73 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import "root:/modules/common" +import "root:/modules/common/functions/file_utils.js" as FileUtils +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +/** + * A service that provides access to Hyprland keybinds. + * Uses the `get_keybinds.py` script to parse comments in config files in a certain format and convert to JSON. + */ +Singleton { + id: root + property string keybindParserPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/hyprland/get_keybinds.py`) + property string defaultKeybindConfigPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/hyprland/keybinds.conf`) + property string userKeybindConfigPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/custom/keybinds.conf`) + property var defaultKeybinds: {"children": []} + property var userKeybinds: {"children": []} + property var keybinds: ({ + children: [ + ...(defaultKeybinds.children ?? []), + ...(userKeybinds.children ?? []), + ] + }) + + Connections { + target: Hyprland + + function onRawEvent(event) { + if (event.name == "configreloaded") { + getDefaultKeybinds.running = true + getUserKeybinds.running = true + } + } + } + + Process { + id: getDefaultKeybinds + running: true + command: [root.keybindParserPath, "--path", root.defaultKeybindConfigPath,] + + stdout: SplitParser { + onRead: data => { + try { + root.defaultKeybinds = JSON.parse(data) + } catch (e) { + console.error("[CheatsheetKeybinds] Error parsing keybinds:", e) + } + } + } + } + + Process { + id: getUserKeybinds + running: true + command: [root.keybindParserPath, "--path", root.userKeybindConfigPath] + + stdout: SplitParser { + onRead: data => { + try { + root.userKeybinds = JSON.parse(data) + } catch (e) { + console.error("[CheatsheetKeybinds] Error parsing keybinds:", e) + } + } + } + } +} + diff --git a/config/quickshell/services/MaterialThemeLoader.qml b/config/quickshell/services/MaterialThemeLoader.qml new file mode 100644 index 00000000..cd4eb686 --- /dev/null +++ b/config/quickshell/services/MaterialThemeLoader.qml @@ -0,0 +1,58 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import "root:/modules/common" +import QtQuick +import Quickshell +import Quickshell.Io + +/** + * Automatically reloads generated material colors. + * It is necessary to run reapplyTheme() on startup because Singletons are lazily loaded. + */ +Singleton { + id: root + property string filePath: Directories.generatedMaterialThemePath + + function reapplyTheme() { + themeFileView.reload() + } + + function applyColors(fileContent) { + const json = JSON.parse(fileContent) + for (const key in json) { + if (json.hasOwnProperty(key)) { + // Convert snake_case to CamelCase + const camelCaseKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase()) + const m3Key = `m3${camelCaseKey}` + Appearance.m3colors[m3Key] = json[key] + } + } + + Appearance.m3colors.darkmode = (Appearance.m3colors.m3background.hslLightness < 0.5) + } + + Timer { + id: delayedFileRead + interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100 + repeat: false + running: false + onTriggered: { + root.applyColors(themeFileView.text()) + } + } + + FileView { + id: themeFileView + path: Qt.resolvedUrl(root.filePath) + watchChanges: true + onFileChanged: { + this.reload() + delayedFileRead.start() + } + onLoadedChanged: { + const fileContent = themeFileView.text() + root.applyColors(fileContent) + } + } +} diff --git a/config/quickshell/shell.qml b/config/quickshell/shell.qml new file mode 100644 index 00000000..36842c2b --- /dev/null +++ b/config/quickshell/shell.qml @@ -0,0 +1,27 @@ +//@ pragma UseQApplication +//@ pragma Env QS_NO_RELOAD_POPUP=1 +//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic + +import "./modules/common/" +import "./modules/overview/" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import Quickshell +import "./services/" + +ShellRoot { + // Enable/disable modules here. False = not loaded at all, so rest assured + // no unnecessary stuff will take up memory if you decide to only use, say, the overview. + property bool enableOverview: true + + // Force initialization of some singletons + Component.onCompleted: { + MaterialThemeLoader.reapplyTheme() + ConfigLoader.loadConfig() + } + + Loader { active: enableOverview; sourceComponent: Overview {} } + +} \ No newline at end of file diff --git a/config/wallust/templates/qml_color.json b/config/wallust/templates/qml_color.json new file mode 100644 index 00000000..70283080 --- /dev/null +++ b/config/wallust/templates/qml_color.json @@ -0,0 +1,21 @@ +{ + "background": "#1e1e2e", + "onBackground": "#bac2de", + "surfaceContainerLow": "{{color4}}", + "surfaceContainer": "{{color6}}", + "surfaceContainerHigh": "{{color3}}", + "surfaceContainerHighest": "{{color2}}", + "onSurface": "#EAE0E7", + "onSurfaceVariant": "#CFC3CD", + "outline": "{{color7}}", + "scrim": "#000000", + "shadow": "#000000", + "primary": "{{color7}}", + "primaryContainer": "{{color7}}", + "secondary": "#D5C0D7", + "secondaryContainer": "{{color5}}", + "onPrimary": "#FFFFFF", + "onPrimaryContainer": "#21005D", + "onSecondaryContainer": "#F2DCF3", + "outlineVariant": "{{color5}}" +} \ No newline at end of file diff --git a/config/wallust/wallust.toml b/config/wallust/wallust.toml index a7f66721..d1f40ab2 100644 --- a/config/wallust/wallust.toml +++ b/config/wallust/wallust.toml @@ -49,6 +49,9 @@ waybar.target = '~/.config/waybar/wallust/colors-waybar.css' kitty.template = 'colors-kitty.conf' kitty.target = '~/.config/kitty/kitty-themes/01-Wallust.conf' +quickshell.template = 'qml_color.json' +quickshell.target = '~/.config/quickshell/qml_color.json' + #swaync.template = 'colors-swaync.css' #swaync.target = '~/.config/swaync/wallust/colors-wallust.css' -- cgit v1.2.3 From ae78ca7ad6ff17b64125787b31c913d0ab873890 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Mon, 9 Jun 2025 11:34:52 +0530 Subject: quickshell startup --- config/hypr/UserConfigs/Startup_Apps.conf | 1 + 1 file changed, 1 insertion(+) (limited to 'config') diff --git a/config/hypr/UserConfigs/Startup_Apps.conf b/config/hypr/UserConfigs/Startup_Apps.conf index 702f9a90..91ff9cd0 100644 --- a/config/hypr/UserConfigs/Startup_Apps.conf +++ b/config/hypr/UserConfigs/Startup_Apps.conf @@ -30,6 +30,7 @@ exec-once = swaync #exec-once = blueman-applet #exec-once = rog-control-center exec-once = waybar +exec-once = qs #clipboard manager exec-once = wl-paste --type text --watch cliphist store -- cgit v1.2.3 From 3bda8183fccb41b67ccf9f02591249f805c94c88 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Mon, 9 Jun 2025 11:52:04 +0530 Subject: Removed hardcoded path --- config/quickshell/modules/common/Appearance.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/quickshell/modules/common/Appearance.qml b/config/quickshell/modules/common/Appearance.qml index 45a4894d..29eca00c 100644 --- a/config/quickshell/modules/common/Appearance.qml +++ b/config/quickshell/modules/common/Appearance.qml @@ -20,7 +20,7 @@ Singleton { property real workpaceTransparency: 0.8 // property real transparency: 0.15 // property real contentTransparency: 0.5 - property string background_image: "file:///home/itachi/.config/rofi/.current_wallpaper" + property string background_image: Directories.config + "/rofi/.current_wallpaper" m3colors: QtObject { property bool darkmode: true -- cgit v1.2.3 From d46077fe5ac56afbd63dc1222e649f10435b05e2 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Mon, 9 Jun 2025 12:02:14 +0530 Subject: Enabled search --- config/quickshell/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/quickshell/config.json b/config/quickshell/config.json index f98f9f69..33fca8e9 100644 --- a/config/quickshell/config.json +++ b/config/quickshell/config.json @@ -24,7 +24,7 @@ "arbitraryRaceConditionDelay": 20 }, "search": { - "searchEnabled": false, + "searchEnabled": true, "nonAppResultDelay": 30, "prefix": { "action": "/", -- cgit v1.2.3 From ab03e009ba48f96b6b6bd4fa0d040e48f404d5d2 Mon Sep 17 00:00:00 2001 From: Lars Müller Date: Thu, 12 Jun 2025 18:12:36 +0200 Subject: add: better rofi theme selector --- config/hypr/scripts/RofiThemeSelector.sh | 207 +++++++++++++++++++++---------- 1 file changed, 143 insertions(+), 64 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/RofiThemeSelector.sh b/config/hypr/scripts/RofiThemeSelector.sh index 6fd8a6f8..8423b2fe 100755 --- a/config/hypr/scripts/RofiThemeSelector.sh +++ b/config/hypr/scripts/RofiThemeSelector.sh @@ -1,75 +1,154 @@ #!/bin/bash -# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ## -# Script for adding a selected theme to the Rofi config - -IFS=$'\n\t' - -# Define directories and variables -rofi_theme_dir="$HOME/.config/rofi/themes" -rofi_config_file="$HOME/.config/rofi/config.rasi" -SED=$(which sed) -iDIR="$HOME/.config/swaync/images" -rofi_theme="$HOME/.config/rofi/config-rofi-theme.rasi" - -# Function to display menu options -menu() { - options=() - while IFS= read -r file; do - options+=("$file") - done < <(find -L "$rofi_theme_dir" -maxdepth 1 -type f -exec basename {} \; | sort -V) - - printf '%s\n' "${options[@]}" -} +# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */  # +# Rofi Themes - Script to preview and apply themes by live-reloading the config. -# Function to add or update theme in the config.rasi -add_theme_to_config() { - local theme_name="$1" - local theme_path="$rofi_theme_dir/$theme_name" - - # if config in $HOME to write as $HOME - if [[ "$theme_path" == $HOME/* ]]; then - theme_path_with_tilde="~${theme_path#$HOME}" - else - theme_path_with_tilde="$theme_path" - fi +# --- Configuration --- +ROFI_THEMES_DIR_CONFIG="$HOME/.config/rofi/themes" +ROFI_THEMES_DIR_LOCAL="$HOME/.local/share/rofi/themes" +ROFI_CONFIG_FILE="$HOME/.config/rofi/config.rasi" +ROFI_THEME_FOR_THIS_SCRIPT="$HOME/.config/rofi/config-rofi-theme.rasi" # A separate rofi theme for the picker itself +IDIR="$HOME/.config/swaync/images" # For notifications - # If no @theme is in the file, add it - if ! grep -q '^\s*@theme' "$rofi_config_file"; then - echo -e "\n\n@theme \"$theme_path_with_tilde\"" >> "$rofi_config_file" - echo "Added @theme \"$theme_path_with_tilde\" to $rofi_config_file" - else - $SED -i "s/^\(\s*@theme.*\)/\/\/\1/" "$rofi_config_file" - echo -e "@theme \"$theme_path_with_tilde\"" >> "$rofi_config_file" - echo "Updated @theme line to $theme_path_with_tilde" - fi +# --- Helper Functions --- - # Ensure no more than max # of lines with //@theme lines - max_line="9" - total_lines=$(grep -c '^\s*//@theme' "$rofi_config_file") - - if [ "$total_lines" -gt "$max_line" ]; then - excess=$((total_lines - max_line)) - # Remove the oldest or the very top //@theme lines - for i in $(seq 1 "$excess"); do - $SED -i '0,/^\s*\/\/@theme/ { /^\s*\/\/@theme/ {d; q; }}' "$rofi_config_file" - done - echo "Removed excess //@theme lines" - fi +# Function to send a notification +notify_user() { + notify-send -u low -i "$1" "$2" "$3" } -# Main function -main() { - choice=$(menu | rofi rofi -dmenu -i -config $rofi_theme) +# Function to apply the selected rofi theme to the main config file +apply_rofi_theme_to_config() { + local theme_name_to_apply="$1" - if [[ -z "$choice" ]]; then - exit 0 - fi - add_theme_to_config "$choice" - notify-send -i "$iDIR/ja.png" -u low 'Rofi Theme applied:' "$choice" + # Find the full path of the theme file + local theme_path + if [[ -f "$ROFI_THEMES_DIR_CONFIG/$theme_name_to_apply" ]]; then + theme_path="$ROFI_THEMES_DIR_CONFIG/$theme_name_to_apply" + elif [[ -f "$ROFI_THEMES_DIR_LOCAL/$theme_name_to_apply" ]]; then + theme_path="$ROFI_THEMES_DIR_LOCAL/$theme_name_to_apply" + else + notify_user "$IDIR/error.png" "Error" "Theme file not found: $theme_name_to_apply" + return 1 + fi + + # Use ~ for the home directory in the config path + local theme_path_with_tilde="~${theme_path#$HOME}" + + # Create a temporary file to safely edit the config + local temp_rofi_config_file + temp_rofi_config_file=$(mktemp) + cp "$ROFI_CONFIG_FILE" "$temp_rofi_config_file" + + # Comment out any existing @theme entry + sed -i -E 's/^(\s*@theme)/\\/\\/\1/' "$temp_rofi_config_file" + + # Add the new @theme entry at the end of the file + echo "@theme \"$theme_path_with_tilde\"" >>"$temp_rofi_config_file" + + # Overwrite the original config file + cp "$temp_rofi_config_file" "$ROFI_CONFIG_FILE" + rm "$temp_rofi_config_file" + + # Prune old commented-out theme lines to prevent clutter + local max_lines=10 + local total_lines=$(grep -c '^//\s*@theme' "$ROFI_CONFIG_FILE") + if [ "$total_lines" -gt "$max_lines" ]; then + local excess=$((total_lines - max_lines)) + for ((i = 1; i <= excess; i++)); do + sed -i '0,/^\s*\/\/@theme/s///' "$ROFI_CONFIG_FILE" + done + fi + + return 0 } -if pgrep -x "rofi" >/dev/null; then - pkill rofi +# --- Main Script Execution --- + +# Check for required directories and files +if [ ! -d "$ROFI_THEMES_DIR_CONFIG" ] && [ ! -d "$ROFI_THEMES_DIR_LOCAL" ]; then + notify_user "$IDIR/error.png" "E-R-R-O-R" "No Rofi themes directory found." + exit 1 +fi + +if [ ! -f "$ROFI_CONFIG_FILE" ]; then + notify_user "$IDIR/error.png" "E-R-R-O-R" "Rofi config file not found: $ROFI_CONFIG_FILE" + exit 1 fi -main +# Backup the original config content +original_rofi_config_content_backup=$(cat "$ROFI_CONFIG_FILE") + +# Generate a sorted list of available theme file names +mapfile -t available_theme_names < <(( + find "$ROFI_THEMES_DIR_CONFIG" -maxdepth 1 -name "*.rasi" -type f -printf "%f\n" 2>/dev/null + find "$ROFI_THEMES_DIR_LOCAL" -maxdepth 1 -name "*.rasi" -type f -printf "%f\n" 2>/dev/null +) | sort -u) + +if [ ${#available_theme_names[@]} -eq 0 ]; then + notify_user "$IDIR/error.png" "No Rofi Themes" "No .rasi files found in theme directories." + exit 1 +fi + +# Find the currently active theme to set as the initial selection +current_selection_index=0 +current_active_theme_path=$(grep -oP '^\s*@theme\s*"\K[^"]+' "$ROFI_CONFIG_FILE" | tail -n 1) +if [ -n "$current_active_theme_path" ]; then + current_active_theme_name=$(basename "$current_active_theme_path") + for i in "${!available_theme_names[@]}"; do + if [[ "${available_theme_names[$i]}" == "$current_active_theme_name" ]]; then + current_selection_index=$i + break + fi + done +fi + +# Main preview loop +while true; do + theme_to_preview_now="${available_theme_names[$current_selection_index]}" + + # Apply the theme for preview + if ! apply_rofi_theme_to_config "$theme_to_preview_now"; then + echo "$original_rofi_config_content_backup" >"$ROFI_CONFIG_FILE" + notify_user "$IDIR/error.png" "Preview Error" "Failed to apply $theme_to_preview_now. Reverted." + exit 1 + fi + + # Prepare theme list for Rofi + rofi_input_list="" + for theme_name_in_list in "${available_theme_names[@]}"; do + rofi_input_list+="$(basename "$theme_name_in_list" .rasi)\n" + done + rofi_input_list_trimmed="${rofi_input_list%\\n}" + + # Launch Rofi and get user's choice + chosen_index_from_rofi=$(echo -e "$rofi_input_list_trimmed" | + rofi -dmenu -i \ + -format 'i' \ + -p "Rofi Theme" \ + -mesg "Preview: $(basename "$theme_to_preview_now" .rasi) | Enter: Preview | Ctrl+S: Apply & Exit | Esc: Cancel" \ + -config "$ROFI_THEME_FOR_THIS_SCRIPT" \ + -selected-row "$current_selection_index" \ + -kb-custom-1 "Control+s") + + rofi_exit_code=$? + + # Handle Rofi's exit code + if [ $rofi_exit_code -eq 0 ]; then # Enter + if [[ "$chosen_index_from_rofi" =~ ^[0-9]+$ ]] && [ "$chosen_index_from_rofi" -lt "${#available_theme_names[@]}" ]; then + current_selection_index="$chosen_index_from_rofi" + fi + elif [ $rofi_exit_code -eq 1 ]; then # Escape + notify_user "$IDIR/note.png" "Rofi Theme" "Selection cancelled. Reverting to original theme." + echo "$original_rofi_config_content_backup" >"$ROFI_CONFIG_FILE" + break + elif [ $rofi_exit_code -eq 10 ]; then # Custom bind 1 (Ctrl+S) + notify_user "$IDIR/ja.png" "Rofi Theme Applied" "$(basename "$theme_to_preview_now" .rasi)" + break + else # Error or unexpected exit code + notify_user "$IDIR/error.png" "Rofi Error" "Unexpected Rofi exit ($rofi_exit_code). Reverting." + echo "$original_rofi_config_content_backup" >"$ROFI_CONFIG_FILE" + break + fi +done + +exit 0 -- cgit v1.2.3 From 1462996cc5256e6337eafc226423de5559214c7f Mon Sep 17 00:00:00 2001 From: Kiran George Date: Mon, 16 Jun 2025 08:15:18 +0530 Subject: Expose font size and family in config --- config/quickshell/config.json | 24 ++++++++++++- config/quickshell/modules/common/ConfigOptions.qml | 1 + .../quickshell/modules/overview/OverviewWidget.qml | 4 ++- config/quickshell/services/ConfigLoader.qml | 39 +++++++++++++++++++--- 4 files changed, 62 insertions(+), 6 deletions(-) (limited to 'config') diff --git a/config/quickshell/config.json b/config/quickshell/config.json index 33fca8e9..f6c9e843 100644 --- a/config/quickshell/config.json +++ b/config/quickshell/config.json @@ -15,7 +15,8 @@ "numOfCols": 5, "showXwaylandIndicator": true, "windowPadding": 6, - "position": 1 + "position": 1, + "workspaceNumberSize": 220 }, "resources": { "updateInterval": 3000 @@ -34,5 +35,26 @@ }, "bar": { "bottom": false + }, + "font": { + "family": { + "main": "Open Sans", + "title": "JetBrains Mono NF", + "iconMaterial": "FiraConde Nerd Font", + "iconNerd": "SpaceMono NF", + "monospace": "JetBrains Mono NF", + "reading": "Readex Pro" + }, + "pixelSize": { + "smallest": 10, + "smaller": 13, + "small": 15, + "normal": 16, + "large": 17, + "larger": 19, + "huge": 22, + "hugeass": 23, + "title": 28 + } } } \ No newline at end of file diff --git a/config/quickshell/modules/common/ConfigOptions.qml b/config/quickshell/modules/common/ConfigOptions.qml index 61e6ab8e..25f0de05 100644 --- a/config/quickshell/modules/common/ConfigOptions.qml +++ b/config/quickshell/modules/common/ConfigOptions.qml @@ -16,6 +16,7 @@ Singleton { property bool showXwaylandIndicator: true property real windowPadding: 6 property real position: 1 // 0: top | 1: middle | 2: bottom + property real workspaceNumberSize: 120 // Set 0, dynamic calculation based on monitor size } property QtObject resources: QtObject { diff --git a/config/quickshell/modules/overview/OverviewWidget.qml b/config/quickshell/modules/overview/OverviewWidget.qml index 73dbbc26..63633602 100644 --- a/config/quickshell/modules/overview/OverviewWidget.qml +++ b/config/quickshell/modules/overview/OverviewWidget.qml @@ -35,7 +35,9 @@ Item { ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale)) property real workspaceNumberMargin: 80 - property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale + property real workspaceNumberSize: (ConfigOptions.overview.workspaceNumberSize > 0) + ? ConfigOptions.overview.workspaceNumberSize + : Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale property int workspaceZ: 0 property int windowZ: 1 property int windowDraggingZ: 99999 diff --git a/config/quickshell/services/ConfigLoader.qml b/config/quickshell/services/ConfigLoader.qml index 051162d1..5f16bf55 100644 --- a/config/quickshell/services/ConfigLoader.qml +++ b/config/quickshell/services/ConfigLoader.qml @@ -29,7 +29,32 @@ Singleton { try { const json = JSON.parse(fileContent); - ObjectUtils.applyToQtObject(ConfigOptions, json); + // Extract font configuration if it exists + let fontConfig = null; + let configForOptions = {}; + + // Copy all properties except font to configForOptions + for (let key in json) { + if (key !== "font") { + configForOptions[key] = json[key]; + } else { + fontConfig = json[key]; + } + } + + // Apply the non-font configuration to ConfigOptions + ObjectUtils.applyToQtObject(ConfigOptions, configForOptions); + + // Apply font configuration to Appearance if it exists + if (fontConfig && typeof Appearance !== 'undefined') { + if (fontConfig.family && Appearance.font && Appearance.font.family) { + ObjectUtils.applyToQtObject(Appearance.font.family, fontConfig.family); + } + if (fontConfig.pixelSize && Appearance.font && Appearance.font.pixelSize) { + ObjectUtils.applyToQtObject(Appearance.font.pixelSize, fontConfig.pixelSize); + } + } + if (root.firstLoad) { root.firstLoad = false; } else { @@ -39,13 +64,19 @@ Singleton { console.error("[ConfigLoader] Error reading file:", e); Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) return; - } } function setLiveConfigValue(nestedKey, value) { let keys = nestedKey.split("."); - let obj = ConfigOptions; + let targetObject = ConfigOptions; + + // Check if this is a font-related configuration + if (keys[0] === "font" && typeof Appearance !== 'undefined') { + targetObject = Appearance; + } + + let obj = targetObject; let parents = [obj]; // Traverse and collect parent objects @@ -113,4 +144,4 @@ Singleton { } } } -} +} \ No newline at end of file -- cgit v1.2.3 From 0cda8f13953d0f4cc6126d4810c04452cc3375b8 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sat, 21 Jun 2025 17:26:23 +0530 Subject: Refactored for better colour and font expose and cleaned up unused code --- config/quickshell/.claude/settings.local.json | 9 ++ config/quickshell/GlobalStates.qml | 2 - config/quickshell/config.json | 25 ++-- config/quickshell/modules/common/Appearance.qml | 75 +++++------- config/quickshell/modules/common/Directories.qml | 3 +- .../modules/common/functions/string_utils.js | 135 --------------------- .../modules/common/widgets/DialogButton.qml | 4 +- .../modules/common/widgets/MaterialSymbol.qml | 9 +- .../modules/common/widgets/StyledText.qml | 7 +- .../modules/common/widgets/StyledTextArea.qml | 10 +- .../modules/common/widgets/StyledToolTip.qml | 2 +- .../quickshell/modules/overview/OverviewWidget.qml | 6 +- .../quickshell/modules/overview/OverviewWindow.qml | 8 +- config/quickshell/modules/overview/SearchItem.qml | 24 ++-- .../quickshell/modules/overview/SearchWidget.qml | 18 +-- config/quickshell/qml_color.json | 17 +++ config/quickshell/services/ConfigLoader.qml | 5 +- config/quickshell/services/MaterialThemeLoader.qml | 2 +- config/quickshell/shell.qml | 1 - config/wallust/templates/qml_color.json | 34 +++--- 20 files changed, 128 insertions(+), 268 deletions(-) create mode 100644 config/quickshell/.claude/settings.local.json create mode 100644 config/quickshell/qml_color.json (limited to 'config') diff --git a/config/quickshell/.claude/settings.local.json b/config/quickshell/.claude/settings.local.json new file mode 100644 index 00000000..d474da6e --- /dev/null +++ b/config/quickshell/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(find:*)", + "Bash(grep:*)" + ], + "deny": [] + } +} \ No newline at end of file diff --git a/config/quickshell/GlobalStates.qml b/config/quickshell/GlobalStates.qml index 7875645c..84b53c03 100644 --- a/config/quickshell/GlobalStates.qml +++ b/config/quickshell/GlobalStates.qml @@ -1,8 +1,6 @@ import "root:/modules/common/" import QtQuick import Quickshell -import Quickshell.Hyprland -import Quickshell.Io pragma Singleton pragma ComponentBehavior: Bound diff --git a/config/quickshell/config.json b/config/quickshell/config.json index f6c9e843..cd9c2c4d 100644 --- a/config/quickshell/config.json +++ b/config/quickshell/config.json @@ -16,7 +16,7 @@ "showXwaylandIndicator": true, "windowPadding": 6, "position": 1, - "workspaceNumberSize": 220 + "workspaceNumberSize": 0 }, "resources": { "updateInterval": 3000 @@ -38,23 +38,16 @@ }, "font": { "family": { - "main": "Open Sans", - "title": "JetBrains Mono NF", - "iconMaterial": "FiraConde Nerd Font", - "iconNerd": "SpaceMono NF", - "monospace": "JetBrains Mono NF", - "reading": "Readex Pro" + "uiFont": "Open Sans", + "iconFont": "FiraConde Nerd Font", + "codeFont": "JetBrains Mono NF" }, "pixelSize": { - "smallest": 10, - "smaller": 13, - "small": 15, - "normal": 16, - "large": 17, - "larger": 19, - "huge": 22, - "hugeass": 23, - "title": 28 + "textSmall": 13, + "textBase": 15, + "textMedium": 16, + "textLarge": 19, + "iconLarge": 22 } } } \ No newline at end of file diff --git a/config/quickshell/modules/common/Appearance.qml b/config/quickshell/modules/common/Appearance.qml index 29eca00c..675f1d1e 100644 --- a/config/quickshell/modules/common/Appearance.qml +++ b/config/quickshell/modules/common/Appearance.qml @@ -18,53 +18,47 @@ Singleton { property real transparency: 0.5 property real contentTransparency: 0.1 property real workpaceTransparency: 0.8 - // property real transparency: 0.15 - // property real contentTransparency: 0.5 property string background_image: Directories.config + "/rofi/.current_wallpaper" m3colors: QtObject { property bool darkmode: true property bool transparent: true - property color m3background: "#161217" - property color m3onBackground: "#EAE0E7" - property color m3surfaceContainerLow: "#1F1A1F" - property color m3surfaceContainer: "#231E23" - property color m3surfaceContainerHigh: "#2D282E" - property color m3surfaceContainerHighest: "#383339" - property color m3onSurface: "#EAE0E7" - property color m3onSurfaceVariant: "#CFC3CD" - property color m3outline: "#cba6f7" - property color m3scrim: "#000000" - property color m3shadow: "#000000" - property color m3primary: "#E5B6F2" - property color m3primaryContainer: "#5D386A" - property color m3secondary: "#D5C0D7" - property color m3secondaryContainer: "#534457" - property color m3onPrimary: "#452152" - property color m3onPrimaryContainer: "#F9D8FF" - property color m3onSecondaryContainer: "#F2DCF3" - property color m3outlineVariant: "#4C444D" + property color m3windowBackground: "#161217" + property color m3primaryText: "#EAE0E7" + property color m3layerBackground1: "#1F1A1F" + property color m3layerBackground2: "#231E23" + property color m3layerBackground3: "#2D282E" + property color m3surfaceText: "#EAE0E7" + property color m3secondaryText: "#CFC3CD" + property color m3borderPrimary: "#cba6f7" + property color m3shadowColor: "#000000" + property color m3accentPrimary: "#E5B6F2" + property color m3accentSecondary: "#D5C0D7" + property color m3selectionBackground: "#534457" + property color m3accentPrimaryText: "#452152" + property color m3selectionText: "#F2DCF3" + property color m3borderSecondary: "#4C444D" property color colTooltip: "#1e1e2e" property color colOnTooltip: "#F8F9FA" } colors: QtObject { - property color colSubtext: m3colors.m3outline - property color colLayer0: ColorUtils.transparentize(m3colors.m3background, root.transparency) - property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.7), root.contentTransparency); - property color colOnLayer1: m3colors.m3onSurfaceVariant; - property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55), root.contentTransparency) - property color colOnLayer2: m3colors.m3onSurface; + property color colSubtext: m3colors.m3borderPrimary + property color colLayer0: ColorUtils.transparentize(m3colors.m3windowBackground, root.transparency) + property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3layerBackground1, m3colors.m3windowBackground, 0.7), root.contentTransparency); + property color colOnLayer1: m3colors.m3secondaryText; + property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3layerBackground2, m3colors.m3layerBackground3, 0.55), root.contentTransparency) + property color colOnLayer2: m3colors.m3surfaceText; property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency) property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency); property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency) property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency); - property color colPrimary: m3colors.m3primary + property color colPrimary: m3colors.m3accentPrimary property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87) property color colPrimaryActive: ColorUtils.mix(colors.colPrimary, colLayer1Active, 0.7) - property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7) + property color colShadow: ColorUtils.transparentize(m3colors.m3shadowColor, 0.7) } rounding: QtObject { @@ -82,23 +76,16 @@ Singleton { font: QtObject { property QtObject family: QtObject { - property string main: "Open Sans" - property string title: "JetBrains Mono NF" - property string iconMaterial: "FiraConde Nerd Font" - property string iconNerd: "SpaceMono NF" - property string monospace: "JetBrains Mono NF" - property string reading: "Readex Pro" + property string uiFont: "Open Sans" + property string iconFont: "FiraConde Nerd Font" + property string codeFont: "JetBrains Mono NF" } property QtObject pixelSize: QtObject { - property int smallest: 10 - property int smaller: 13 - property int small: 15 - property int normal: 16 - property int large: 17 - property int larger: 19 - property int huge: 22 - property int hugeass: 23 - property int title: 28 + property int textSmall: 13 + property int textBase: 15 + property int textMedium: 16 + property int textLarge: 19 + property int iconLarge: 22 } } diff --git a/config/quickshell/modules/common/Directories.qml b/config/quickshell/modules/common/Directories.qml index 9ddf43bd..694c73df 100644 --- a/config/quickshell/modules/common/Directories.qml +++ b/config/quickshell/modules/common/Directories.qml @@ -11,9 +11,10 @@ Singleton { // XDG Dirs, with "file://" readonly property string config: StandardPaths.standardLocations(StandardPaths.ConfigLocation)[0] readonly property string state: StandardPaths.standardLocations(StandardPaths.StateLocation)[0] + readonly property string gen_cache: StandardPaths.standardLocations(StandardPaths.GenericCacheLocation)[0] // Other dirs used by the shell, without "file://" property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/quickshell`) property string shellConfigPath: `${Directories.shellConfig}/config.json` - property string generatedMaterialThemePath: `${Directories.shellConfig}/qml_color.json` + property string generatedMaterialThemePath: `${Directories.gen_cache}/hellwal/qml_color.json` } diff --git a/config/quickshell/modules/common/functions/string_utils.js b/config/quickshell/modules/common/functions/string_utils.js index c22671eb..c31edf49 100644 --- a/config/quickshell/modules/common/functions/string_utils.js +++ b/config/quickshell/modules/common/functions/string_utils.js @@ -42,141 +42,6 @@ function shellSingleQuoteEscape(str) { .replace(/'/g, "'\\''"); } -/** - * Splits markdown blocks into three different types: text, think, and code. - * @param { string } markdown - */ -function splitMarkdownBlocks(markdown) { - const regex = /```(\w+)?\n([\s\S]*?)```|([\s\S]*?)<\/think>/g; - /** - * @type {{type: "text" | "think" | "code"; content: string; lang: string | undefined; completed: boolean | undefined}[]} - */ - let result = []; - let lastIndex = 0; - let match; - while ((match = regex.exec(markdown)) !== null) { - if (match.index > lastIndex) { - const text = markdown.slice(lastIndex, match.index); - if (text.trim()) { - result.push({ type: "text", content: text }); - } - } - if (match[0].startsWith('```')) { - if (match[2] && match[2].trim()) { - result.push({ type: "code", lang: match[1] || "", content: match[2], completed: true }); - } - } else if (match[0].startsWith('')) { - if (match[3] && match[3].trim()) { - result.push({ type: "think", content: match[3], completed: true }); - } - } - lastIndex = regex.lastIndex; - } - // Handle any remaining text after the last match - if (lastIndex < markdown.length) { - const text = markdown.slice(lastIndex); - // Check for unfinished block - const thinkStart = text.indexOf(''); - const codeStart = text.indexOf('```'); - if ( - thinkStart !== -1 && - (codeStart === -1 || thinkStart < codeStart) - ) { - const beforeThink = text.slice(0, thinkStart); - if (beforeThink.trim()) { - result.push({ type: "text", content: beforeThink }); - } - const thinkContent = text.slice(thinkStart + 7); - if (thinkContent.trim()) { - result.push({ type: "think", content: thinkContent, completed: false }); - } - } else if (codeStart !== -1) { - const beforeCode = text.slice(0, codeStart); - if (beforeCode.trim()) { - result.push({ type: "text", content: beforeCode }); - } - // Try to detect language after ``` - const codeLangMatch = text.slice(codeStart + 3).match(/^(\w+)?\n/); - let lang = ""; - let codeContentStart = codeStart + 3; - if (codeLangMatch) { - lang = codeLangMatch[1] || ""; - codeContentStart += codeLangMatch[0].length; - } else if (text[codeStart + 3] === '\n') { - codeContentStart += 1; - } - const codeContent = text.slice(codeContentStart); - if (codeContent.trim()) { - result.push({ type: "code", lang, content: codeContent, completed: false }); - } - } else if (text.trim()) { - result.push({ type: "text", content: text }); - } - } - // console.log(JSON.stringify(result, null, 2)); - return result; -} - -/** - * Returns the original string with backslashes escaped - * @param { string } str - * @returns { string } - */ -function escapeBackslashes(str) { - return str.replace(/\\/g, '\\\\'); -} - -/** - * Wraps words to supplied maximum length - * @param { string | null } str - * @param { number } maxLen - * @returns { string } - */ -function wordWrap(str, maxLen) { - if (!str) return ""; - let words = str.split(" "); - let lines = []; - let current = ""; - for (let i = 0; i < words.length; ++i) { - if ((current + (current.length > 0 ? " " : "") + words[i]).length > maxLen) { - if (current.length > 0) lines.push(current); - current = words[i]; - } else { - current += (current.length > 0 ? " " : "") + words[i]; - } - } - if (current.length > 0) lines.push(current); - return lines.join("\n"); -} - -function cleanMusicTitle(title) { - if (!title) return ""; - // Brackets - title = title.replace(/^ *\([^)]*\) */g, " "); // Round brackets - title = title.replace(/^ *\[[^\]]*\] */g, " "); // Square brackets - title = title.replace(/^ *\{[^\}]*\} */g, " "); // Curly brackets - // Japenis brackets - title = title.replace(/^ *【[^】]*】/, "") // Touhou - title = title.replace(/^ *《[^》]*》/, "") // ?? - title = title.replace(/^ *「[^」]*」/, "") // OP/ED - title = title.replace(/^ *『[^』]*』/, "") // OP/ED - - return title; -} - -function friendlyTimeForSeconds(seconds) { - if (isNaN(seconds) || seconds < 0) return "0:00"; - seconds = Math.floor(seconds); - const h = Math.floor(seconds / 3600); - const m = Math.floor((seconds % 3600) / 60); - const s = seconds % 60; - if (h > 0) { - return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; - } else { - return `${m}:${s.toString().padStart(2, '0')}`; - } -} - function escapeHtml(str) { if (typeof str !== 'string') return str; return str diff --git a/config/quickshell/modules/common/widgets/DialogButton.qml b/config/quickshell/modules/common/widgets/DialogButton.qml index 9e19a507..b799336a 100644 --- a/config/quickshell/modules/common/widgets/DialogButton.qml +++ b/config/quickshell/modules/common/widgets/DialogButton.qml @@ -18,7 +18,7 @@ RippleButton { buttonRadius: Appearance?.rounding.full ?? 9999 property color colEnabled: Appearance?.colors.colPrimary - property color colDisabled: Appearance?.m3colors.m3outline + property color colDisabled: Appearance?.m3colors.m3borderPrimary contentItem: StyledText { id: buttonTextWidget @@ -27,7 +27,7 @@ RippleButton { anchors.rightMargin: 15 text: buttonText horizontalAlignment: Text.AlignHCenter - font.pixelSize: Appearance?.font.pixelSize.small ?? 12 + font.pixelSize: Appearance?.font.pixelSize.textBase ?? 12 color: button.enabled ? button.colEnabled : button.colDisabled Behavior on color { diff --git a/config/quickshell/modules/common/widgets/MaterialSymbol.qml b/config/quickshell/modules/common/widgets/MaterialSymbol.qml index dbbfff00..214f838e 100644 --- a/config/quickshell/modules/common/widgets/MaterialSymbol.qml +++ b/config/quickshell/modules/common/widgets/MaterialSymbol.qml @@ -1,17 +1,16 @@ import "root:/modules/common/" import QtQuick -import QtQuick.Layouts Text { id: root - property real iconSize: Appearance?.font.pixelSize.small ?? 16 + property real iconSize: Appearance?.font.pixelSize.textBase ?? 16 property real fill: 0 renderType: Text.NativeRendering font.hintingPreference: Font.PreferFullHinting verticalAlignment: Text.AlignVCenter - font.family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded" + font.family: Appearance?.font.family.iconFont ?? "Material Symbols Rounded" font.pixelSize: iconSize - color: Appearance.m3colors.m3onBackground + color: Appearance.m3colors.m3primaryText Behavior on fill { NumberAnimation { @@ -23,8 +22,6 @@ Text { font.variableAxes: { "FILL": fill, - // "wght": font.weight, - // "GRAD": 0, "opsz": iconSize, } } diff --git a/config/quickshell/modules/common/widgets/StyledText.qml b/config/quickshell/modules/common/widgets/StyledText.qml index 6eef5785..988c136d 100644 --- a/config/quickshell/modules/common/widgets/StyledText.qml +++ b/config/quickshell/modules/common/widgets/StyledText.qml @@ -1,14 +1,13 @@ import "root:/modules/common" import QtQuick -import QtQuick.Layouts Text { renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter font { hintingPreference: Font.PreferFullHinting - family: Appearance?.font.family.main ?? "sans-serif" - pixelSize: Appearance?.font.pixelSize.small ?? 15 + family: Appearance?.font.family.uiFont ?? "sans-serif" + pixelSize: Appearance?.font.pixelSize.textBase ?? 15 } - color: Appearance?.m3colors.m3onBackground ?? "black" + color: Appearance?.m3colors.m3primaryText ?? "black" } diff --git a/config/quickshell/modules/common/widgets/StyledTextArea.qml b/config/quickshell/modules/common/widgets/StyledTextArea.qml index 1ea9a349..af3cf34d 100644 --- a/config/quickshell/modules/common/widgets/StyledTextArea.qml +++ b/config/quickshell/modules/common/widgets/StyledTextArea.qml @@ -4,12 +4,12 @@ import QtQuick.Controls TextArea { renderType: Text.NativeRendering - selectedTextColor: Appearance.m3colors.m3onSecondaryContainer - selectionColor: Appearance.m3colors.m3secondaryContainer - placeholderTextColor: Appearance.m3colors.m3outline + selectedTextColor: Appearance.m3colors.m3selectionText + selectionColor: Appearance.m3colors.m3selectionBackground + placeholderTextColor: Appearance.m3colors.m3borderPrimary font { - family: Appearance?.font.family.main ?? "sans-serif" - pixelSize: Appearance?.font.pixelSize.small ?? 15 + family: Appearance?.font.family.uiFont ?? "sans-serif" + pixelSize: Appearance?.font.pixelSize.textBase ?? 15 hintingPreference: Font.PreferFullHinting } } diff --git a/config/quickshell/modules/common/widgets/StyledToolTip.qml b/config/quickshell/modules/common/widgets/StyledToolTip.qml index 2ca16df1..aaaad813 100644 --- a/config/quickshell/modules/common/widgets/StyledToolTip.qml +++ b/config/quickshell/modules/common/widgets/StyledToolTip.qml @@ -50,7 +50,7 @@ ToolTip { id: tooltipTextObject anchors.centerIn: parent text: content - font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14 + font.pixelSize: Appearance?.font.pixelSize.textSmall ?? 14 font.hintingPreference: Font.PreferNoHinting // Prevent shaky text color: Appearance?.m3colors.colOnTooltip ?? "#FFFFFF" wrapMode: Text.Wrap diff --git a/config/quickshell/modules/overview/OverviewWidget.qml b/config/quickshell/modules/overview/OverviewWidget.qml index 63633602..2ea8d58a 100644 --- a/config/quickshell/modules/overview/OverviewWidget.qml +++ b/config/quickshell/modules/overview/OverviewWidget.qml @@ -25,7 +25,7 @@ Item { property var windowAddresses: HyprlandData.addresses property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id) property real scale: ConfigOptions.overview.scale - property color activeBorderColor: Appearance.m3colors.m3secondary + property color activeBorderColor: Appearance.m3colors.m3accentSecondary property real workspaceImplicitWidth: Math.max(100, (monitorData?.transform % 2 === 1) ? ((monitor.height - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) : @@ -71,7 +71,7 @@ Item { property real padding: 10 anchors.fill: parent anchors.margins: Appearance.sizes.elevationMargin - border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.2) + border.color : ColorUtils.transparentize(Appearance.m3colors.m3borderPrimary, 0.2) border.width : 2 implicitWidth: workspaceColumnLayout.implicitWidth + padding * 2 @@ -170,7 +170,7 @@ Item { color: "transparent" radius: parent.radius border.width: 1 - border.color: hoveredWhileDragging ? hoveredBorderColor : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.6) + border.color: hoveredWhileDragging ? hoveredBorderColor : ColorUtils.transparentize(Appearance.m3colors.m3borderPrimary, 0.6) z: 10 // Ensure it's on top } diff --git a/config/quickshell/modules/overview/OverviewWindow.qml b/config/quickshell/modules/overview/OverviewWindow.qml index 273eff7e..449a98c4 100644 --- a/config/quickshell/modules/overview/OverviewWindow.qml +++ b/config/quickshell/modules/overview/OverviewWindow.qml @@ -32,7 +32,7 @@ Rectangle { // Window property var xwaylandIndicatorToIconRatio: 0.35 property var iconToWindowRatioCompact: 0.6 property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing") - property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth + property bool compactMode: Appearance.font.pixelSize.textSmall * 4 > targetWindowHeight || Appearance.font.pixelSize.textSmall * 4 > targetWindowWidth property bool indicateXWayland: (ConfigOptions.overview.showXwaylandIndicator && windowData?.xwayland) ?? false @@ -44,7 +44,7 @@ Rectangle { // Window radius: Appearance.rounding.windowRounding * root.scale color: pressed ? Appearance.colors.colLayer2Active : hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2 // border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.9) - border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.4) + border.color : ColorUtils.transparentize(Appearance.m3colors.m3borderPrimary, 0.4) border.pixelAligned : false border.width : 2 @@ -65,7 +65,7 @@ Rectangle { // Window anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.right: parent.right - spacing: Appearance.font.pixelSize.smaller * 0.5 + spacing: Appearance.font.pixelSize.textSmall * 0.5 IconImage { id: windowIcon @@ -85,7 +85,7 @@ Rectangle { // Window Layout.fillWidth: true Layout.fillHeight: true horizontalAlignment: Text.AlignHCenter - font.pixelSize: Appearance.font.pixelSize.smaller + font.pixelSize: Appearance.font.pixelSize.textSmall font.italic: indicateXWayland ? true : false elide: Text.ElideRight text: windowData?.title ?? "" diff --git a/config/quickshell/modules/overview/SearchItem.qml b/config/quickshell/modules/overview/SearchItem.qml index 1363b88d..1357d03c 100644 --- a/config/quickshell/modules/overview/SearchItem.qml +++ b/config/quickshell/modules/overview/SearchItem.qml @@ -22,7 +22,7 @@ RippleButton { property string itemName: entry?.name property string itemIcon: entry?.icon ?? "" property var itemExecute: entry?.execute - property string fontType: entry?.fontType ?? "main" + property string fontType: entry?.fontType ?? "uiFont" property string itemClickActionName: entry?.clickActionName property string bigText: entry?.bigText ?? "" property string materialSymbol: entry?.materialSymbol ?? "" @@ -31,7 +31,7 @@ RippleButton { property string highlightPrefix: `` property string highlightSuffix: `` function highlightContent(content, query) { - if (!query || query.length === 0 || content == query || fontType === "monospace") + if (!query || query.length === 0 || content == query || fontType === "codeFont") return StringUtils.escapeHtml(content); let contentLower = content.toLowerCase(); @@ -80,7 +80,7 @@ RippleButton { buttonRadius: Appearance.rounding.normal colBackground: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active : ((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover : - ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) + ColorUtils.transparentize(Appearance.m3colors.m3layerBackground3, 1)) colBackgroundHover: Appearance.colors.colLayer1Hover colRipple: Appearance.colors.colLayer1Active @@ -140,7 +140,7 @@ RippleButton { MaterialSymbol { text: root.materialSymbol iconSize: 30 - color: Appearance.m3colors.m3onSurface + color: Appearance.m3colors.m3surfaceText } } @@ -148,8 +148,8 @@ RippleButton { id: bigTextComponent StyledText { text: root.bigText - font.pixelSize: Appearance.font.pixelSize.larger - color: Appearance.m3colors.m3onSurface + font.pixelSize: Appearance.font.pixelSize.textLarge + color: Appearance.m3colors.m3surfaceText } } @@ -160,7 +160,7 @@ RippleButton { Layout.alignment: Qt.AlignVCenter spacing: 0 StyledText { - font.pixelSize: Appearance.font.pixelSize.smaller + font.pixelSize: Appearance.font.pixelSize.textSmall color: Appearance.colors.colSubtext visible: root.itemType && root.itemType != qsTr("App") text: root.itemType @@ -178,8 +178,8 @@ RippleButton { id: activeText anchors.centerIn: parent text: "check" - font.pixelSize: Appearance.font.pixelSize.normal - color: Appearance.m3colors.m3onPrimary + font.pixelSize: Appearance.font.pixelSize.textMedium + color: Appearance.m3colors.m3accentPrimaryText } } } @@ -187,9 +187,9 @@ RippleButton { Layout.fillWidth: true id: nameText textFormat: Text.StyledText // RichText also works, but StyledText ensures elide work - font.pixelSize: Appearance.font.pixelSize.small + font.pixelSize: Appearance.font.pixelSize.textBase font.family: Appearance.font.family[root.fontType] - color: Appearance.m3colors.m3onSurface + color: Appearance.m3colors.m3surfaceText horizontalAlignment: Text.AlignLeft elide: Text.ElideRight text: `${root.displayContent}` @@ -211,7 +211,7 @@ RippleButton { Layout.fillWidth: false visible: (root.hovered || root.focus) id: clickAction - font.pixelSize: Appearance.font.pixelSize.normal + font.pixelSize: Appearance.font.pixelSize.textMedium color: Appearance.colors.colSubtext horizontalAlignment: Text.AlignRight text: root.itemClickActionName diff --git a/config/quickshell/modules/overview/SearchWidget.qml b/config/quickshell/modules/overview/SearchWidget.qml index fed710ec..f84aa558 100644 --- a/config/quickshell/modules/overview/SearchWidget.qml +++ b/config/quickshell/modules/overview/SearchWidget.qml @@ -201,8 +201,8 @@ Item { // Wrapper MaterialSymbol { id: searchIcon Layout.leftMargin: 15 - iconSize: Appearance.font.pixelSize.huge - color: Appearance.m3colors.m3onSurface + iconSize: Appearance.font.pixelSize.iconLarge + color: Appearance.m3colors.m3surfaceText text: root.searchingText.startsWith(ConfigOptions.search.prefix.clipboard) ? 'content_paste_search' : '' } TextField { // Search box @@ -213,15 +213,15 @@ Item { // Wrapper padding: 15 renderType: Text.NativeRendering font { - family: Appearance?.font.family.main ?? "sans-serif" - pixelSize: Appearance?.font.pixelSize.small ?? 15 + family: Appearance?.font.family.uiFont ?? "sans-serif" + pixelSize: Appearance?.font.pixelSize.textBase ?? 15 hintingPreference: Font.PreferFullHinting } - color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant - selectedTextColor: Appearance.m3colors.m3onSecondaryContainer - selectionColor: Appearance.m3colors.m3secondaryContainer + color: activeFocus ? Appearance.m3colors.m3surfaceText : Appearance.m3colors.m3secondaryText + selectedTextColor: Appearance.m3colors.m3selectionText + selectionColor: Appearance.m3colors.m3selectionBackground placeholderText: qsTr("Search, calculate or run") - placeholderTextColor: Appearance.m3colors.m3outline + placeholderTextColor: Appearance.m3colors.m3borderPrimary implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth Behavior on implicitWidth { @@ -260,7 +260,7 @@ Item { // Wrapper visible: root.showResults Layout.fillWidth: true height: 1 - color: Appearance.m3colors.m3outlineVariant + color: Appearance.m3colors.m3borderSecondary } ListView { // App results diff --git a/config/quickshell/qml_color.json b/config/quickshell/qml_color.json new file mode 100644 index 00000000..888ba3e6 --- /dev/null +++ b/config/quickshell/qml_color.json @@ -0,0 +1,17 @@ +{ + "windowBackground": "#0f0f15", + "primaryText": "#bac2de", + "layerBackground1": "#1F1A1F", + "layerBackground2": "#231E23", + "layerBackground3": "#2D282E", + "surfaceText": "#EAE0E7", + "secondaryText": "#CFC3CD", + "borderPrimary": "#cba6f7", + "shadowColor": "#000000", + "accentPrimary": "#6750A4", + "accentSecondary": "#D5C0D7", + "selectionBackground": "#534457", + "accentPrimaryText": "#FFFFFF", + "selectionText": "#F2DCF3", + "borderSecondary": "#4C444D" +} \ No newline at end of file diff --git a/config/quickshell/services/ConfigLoader.qml b/config/quickshell/services/ConfigLoader.qml index 5f16bf55..d3fb4e26 100644 --- a/config/quickshell/services/ConfigLoader.qml +++ b/config/quickshell/services/ConfigLoader.qml @@ -72,7 +72,7 @@ Singleton { let targetObject = ConfigOptions; // Check if this is a font-related configuration - if (keys[0] === "font" && typeof Appearance !== 'undefined') { + if (keys[0] === "font") { targetObject = Appearance; } @@ -101,13 +101,12 @@ Singleton { } } - console.log(parents.join(".")); console.log(`[ConfigLoader] Setting live config value: ${nestedKey} = ${convertedValue}`); obj[keys[keys.length - 1]] = convertedValue; } function saveConfig() { - const plainConfig = ObjectUtils.toPlainObject(ConfigOptions) + const plainConfig = ObjectUtils.toPlainObject(ConfigOptions); Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(JSON.stringify(plainConfig, null, 2))}' > '${root.filePath}'`) } diff --git a/config/quickshell/services/MaterialThemeLoader.qml b/config/quickshell/services/MaterialThemeLoader.qml index cd4eb686..2d67ad5b 100644 --- a/config/quickshell/services/MaterialThemeLoader.qml +++ b/config/quickshell/services/MaterialThemeLoader.qml @@ -29,7 +29,7 @@ Singleton { } } - Appearance.m3colors.darkmode = (Appearance.m3colors.m3background.hslLightness < 0.5) + Appearance.m3colors.darkmode = (Appearance.m3colors.m3windowBackground.hslLightness < 0.5) } Timer { diff --git a/config/quickshell/shell.qml b/config/quickshell/shell.qml index 36842c2b..b14c8773 100644 --- a/config/quickshell/shell.qml +++ b/config/quickshell/shell.qml @@ -7,7 +7,6 @@ import "./modules/overview/" import QtQuick import QtQuick.Controls import QtQuick.Layouts -import QtQuick.Window import Quickshell import "./services/" diff --git a/config/wallust/templates/qml_color.json b/config/wallust/templates/qml_color.json index 70283080..03565181 100644 --- a/config/wallust/templates/qml_color.json +++ b/config/wallust/templates/qml_color.json @@ -1,21 +1,17 @@ { - "background": "#1e1e2e", - "onBackground": "#bac2de", - "surfaceContainerLow": "{{color4}}", - "surfaceContainer": "{{color6}}", - "surfaceContainerHigh": "{{color3}}", - "surfaceContainerHighest": "{{color2}}", - "onSurface": "#EAE0E7", - "onSurfaceVariant": "#CFC3CD", - "outline": "{{color7}}", - "scrim": "#000000", - "shadow": "#000000", - "primary": "{{color7}}", - "primaryContainer": "{{color7}}", - "secondary": "#D5C0D7", - "secondaryContainer": "{{color5}}", - "onPrimary": "#FFFFFF", - "onPrimaryContainer": "#21005D", - "onSecondaryContainer": "#F2DCF3", - "outlineVariant": "{{color5}}" + "windowBackground": "#0f0f15", + "primaryText": "#bac2de", + "layerBackground1": "{{color7}}", + "layerBackground2": "{{color6}}", + "layerBackground3": "#2D282E", + "surfaceText": "#EAE0E7", + "secondaryText": "#CFC3CD", + "borderPrimary": "{{color7}}", + "shadowColor": "#000000", + "accentPrimary": "{{color7}}", + "accentSecondary": "#{{color7}}", + "selectionBackground": "{{color7}}", + "accentPrimaryText": "#FFFFFF", + "selectionText": "#F2DCF3", + "borderSecondary": "#{{color5}}" } \ No newline at end of file -- cgit v1.2.3 From 7b8e5f9c2f542b70af067d3034524086c8dc800c Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sat, 21 Jun 2025 17:34:03 +0530 Subject: Cleanup --- config/quickshell/.claude/settings.local.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 config/quickshell/.claude/settings.local.json (limited to 'config') diff --git a/config/quickshell/.claude/settings.local.json b/config/quickshell/.claude/settings.local.json deleted file mode 100644 index d474da6e..00000000 --- a/config/quickshell/.claude/settings.local.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(find:*)", - "Bash(grep:*)" - ], - "deny": [] - } -} \ No newline at end of file -- cgit v1.2.3 From 96a6196f835df391d477cca75a5b6725aaf2e464 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sat, 21 Jun 2025 17:35:48 +0530 Subject: Updated workspaceNumberSize from 0->auto to manual->220 --- config/quickshell/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/quickshell/config.json b/config/quickshell/config.json index cd9c2c4d..286985f5 100644 --- a/config/quickshell/config.json +++ b/config/quickshell/config.json @@ -16,7 +16,7 @@ "showXwaylandIndicator": true, "windowPadding": 6, "position": 1, - "workspaceNumberSize": 0 + "workspaceNumberSize": 220 }, "resources": { "updateInterval": 3000 -- cgit v1.2.3 From 897873fccd5d19c460128998b9434238dcf07ba6 Mon Sep 17 00:00:00 2001 From: Kiran George Date: Sun, 22 Jun 2025 08:14:42 +0530 Subject: Corrected qml_color path --- config/quickshell/modules/common/Directories.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/quickshell/modules/common/Directories.qml b/config/quickshell/modules/common/Directories.qml index 694c73df..f4a6bf83 100644 --- a/config/quickshell/modules/common/Directories.qml +++ b/config/quickshell/modules/common/Directories.qml @@ -16,5 +16,5 @@ Singleton { // Other dirs used by the shell, without "file://" property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/quickshell`) property string shellConfigPath: `${Directories.shellConfig}/config.json` - property string generatedMaterialThemePath: `${Directories.gen_cache}/hellwal/qml_color.json` + property string generatedMaterialThemePath: `${Directories.shellConfig}/qml_color.json` } -- cgit v1.2.3 From 22193a572b2672f982fde74e8544c8881caccb6d Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Wed, 25 Jun 2025 16:30:27 +0800 Subject: fix brightness The brightness adjustment response is slow --- config/hypr/scripts/Brightness.sh | 101 ++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 53 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Brightness.sh b/config/hypr/scripts/Brightness.sh index 8e5d525a..3e283c11 100755 --- a/config/hypr/scripts/Brightness.sh +++ b/config/hypr/scripts/Brightness.sh @@ -6,69 +6,64 @@ iDIR="$HOME/.config/swaync/icons" notification_timeout=1000 step=10 # INCREASE/DECREASE BY THIS VALUE -# Get brightness -get_backlight() { - brightnessctl -m | cut -d, -f4 | sed 's/%//' +# Get current brightness as an integer (without %) +get_brightness() { + brightnessctl -m | cut -d, -f4 | tr -d '%' } -# Get icons -get_icon() { - current=$(get_backlight) - if [ "$current" -le "20" ]; then - icon="$iDIR/brightness-20.png" - elif [ "$current" -le "40" ]; then - icon="$iDIR/brightness-40.png" - elif [ "$current" -le "60" ]; then - icon="$iDIR/brightness-60.png" - elif [ "$current" -le "80" ]; then - icon="$iDIR/brightness-80.png" - else - icon="$iDIR/brightness-100.png" - fi +# Determine the icon based on brightness level +get_icon_path() { + local brightness=$1 + local level=$(( (brightness + 19) / 20 * 20 )) # Round up to next 20 + if (( level > 100 )); then + level=100 + fi + echo "$ICON_DIR/brightness-${level}.png" } -# Notify -notify_user() { - notify-send -e -h string:x-canonical-private-synchronous:brightness_notif -h int:value:$current -u low -i $icon "Screen" "Brightness:$current%" +# Send notification +send_notification() { + local brightness=$1 + local icon_path=$2 + + notify-send -e \ + -h string:x-canonical-private-synchronous:brightness_notif \ + -h int:value:"$brightness" \ + -u low \ + -i "$icon_path" \ + "Screen" "Brightness: ${brightness}%" } -# Change brightness -change_backlight() { - local current_brightness - current_brightness=$(get_backlight) +# Change brightness and notify +change_brightness() { + local delta=$1 + local current new icon + + current=$(get_brightness) + new=$((current + delta)) - # Calculate new brightness - if [[ "$1" == "+${step}%" ]]; then - new_brightness=$((current_brightness + step)) - elif [[ "$1" == "${step}%-" ]]; then - new_brightness=$((current_brightness - step)) - fi + # Clamp between 5 and 100 + (( new < 5 )) && new=5 + (( new > 100 )) && new=100 - # Ensure new brightness is within valid range - if (( new_brightness < 5 )); then - new_brightness=5 - elif (( new_brightness > 100 )); then - new_brightness=100 - fi + brightnessctl set "${new}%" - brightnessctl set "${new_brightness}%" - get_icon - current=$new_brightness - notify_user + icon=$(get_icon_path "$new") + send_notification "$new" "$icon" } -# Execute accordingly +# Main case "$1" in - "--get") - get_backlight - ;; - "--inc") - change_backlight "+${step}%" - ;; - "--dec") - change_backlight "${step}%-" - ;; - *) - get_backlight - ;; + "--get") + get_brightness + ;; + "--inc") + change_brightness "$STEP" + ;; + "--dec") + change_brightness "-$STEP" + ;; + *) + get_brightness + ;; esac -- cgit v1.2.3 From ddd606047326a669d0e8e9fdc3f1cd9888eebc83 Mon Sep 17 00:00:00 2001 From: Humblemonk Date: Mon, 30 Jun 2025 12:25:16 -0400 Subject: Update the logout command used by wlogout Minor change to the logout action of wlogout. hyprctl dispatch exit 0 is Hyprland's native logout command. It: - Properly terminates the Hyprland compositor - Allows Hyprland to clean up its resources and state - Handles window cleanup gracefully - Is the intended way to exit according to Hyprland's design This change resolved my issue where clicking on logout could sometimes hardlock my environment and require a forced reboot --- config/wlogout/layout | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'config') diff --git a/config/wlogout/layout b/config/wlogout/layout index ea468745..bcebbf44 100644 --- a/config/wlogout/layout +++ b/config/wlogout/layout @@ -18,7 +18,7 @@ } { "label" : "logout", - "action" : "loginctl kill-session $XDG_SESSION_ID", + "action" : "hyprctl dispatch exit 0", "text" : "Logout", "keybind" : "e" } @@ -33,4 +33,4 @@ "action" : "systemctl hibernate", "text" : "Hibernate", "keybind" : "h" -} \ No newline at end of file +} -- cgit v1.2.3 From a371f3125d2787841b7178d74d2dc89e8c741484 Mon Sep 17 00:00:00 2001 From: brockar Date: Wed, 9 Jul 2025 00:41:09 -0300 Subject: fix: update Element (Matrix) on WindowRules.conf - Add `Element` class recognizer - Include both classes in the `im` tag --- config/hypr/UserConfigs/WindowRules.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index 099cd2c8..81021ae7 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -47,6 +47,7 @@ windowrule = tag +im, class:^([Ww]hatsapp-for-linux)$ windowrule = tag +im, class:^(ZapZap|com.rtosta.zapzap)$ windowrule = tag +im, class:^(org.telegram.desktop|io.github.tdesktop_x64.TDesktop)$ windowrule = tag +im, class:^(teams-for-linux)$ +windowrule = tag +im, class:^(im.riot.Riot|Element)$ # Element Matrix client # game tags windowrule = tag +games, class:^(gamescope)$ @@ -177,7 +178,6 @@ windowrule = opacity 0.82 0.75, tag:viewer* windowrule = opacity 0.9 0.7, tag:wallpaper* windowrule = opacity 0.8 0.7, class:^(gedit|org.gnome.TextEditor|mousepad)$ windowrule = opacity 0.9 0.8, class:^(deluge)$ -windowrule = opacity 0.9 0.8, class:^(im.riot.Riot)$ # Element matrix client windowrule = opacity 0.9 0.8, class:^(seahorse)$ # gnome-keyring gui windowrule = opacity 0.95 0.75, title:^(Picture-in-Picture)$ -- cgit v1.2.3 From b37cf284a9d1ebd6583ba8489195c755c65935d8 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 15 Jul 2025 19:33:34 +0900 Subject: Introducing QuickShell --- config/hypr/UserConfigs/UserSettings.conf | 2 -- config/hypr/scripts/Refresh.sh | 4 +-- config/hypr/scripts/RefreshNoWaybar.sh | 2 +- copy.sh | 51 ++++++++++++++++++++++++++----- 4 files changed, 47 insertions(+), 12 deletions(-) (limited to 'config') diff --git a/config/hypr/UserConfigs/UserSettings.conf b/config/hypr/UserConfigs/UserSettings.conf index 29dbc572..1f8c23c3 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -104,8 +104,6 @@ xwayland { } render { - #explicit_sync = 2 - #explicit_sync_kms = 2 direct_scanout = 0 } diff --git a/config/hypr/scripts/Refresh.sh b/config/hypr/scripts/Refresh.sh index 2d7887a5..d04570b1 100755 --- a/config/hypr/scripts/Refresh.sh +++ b/config/hypr/scripts/Refresh.sh @@ -25,8 +25,8 @@ done # added since wallust sometimes not applying killall -SIGUSR2 waybar -# quit ags & relaunch ags -#ags -q && ags & +# quit quickshell & relaunch quickshell +#pkill qs && qs & # some process to kill for pid in $(pidof waybar rofi swaync ags swaybg); do diff --git a/config/hypr/scripts/RefreshNoWaybar.sh b/config/hypr/scripts/RefreshNoWaybar.sh index e5a0835e..cdbb82db 100755 --- a/config/hypr/scripts/RefreshNoWaybar.sh +++ b/config/hypr/scripts/RefreshNoWaybar.sh @@ -26,7 +26,7 @@ for _prs in "${_ps[@]}"; do done # quit ags & relaunch ags -#ags -q && ags & +#pkill qs && qs & # Wallust refresh ${SCRIPTSDIR}/WallustSwww.sh & diff --git a/copy.sh b/copy.sh index 0c556ddd..f752ac92 100755 --- a/copy.sh +++ b/copy.sh @@ -240,11 +240,11 @@ if command -v blueman-applet >/dev/null 2>&1; then sed -i '/^\s*#exec-once = blueman-applet/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf fi -# Check if ags is installed edit ags behaviour on configs -if command -v ags >/dev/null 2>&1; then - sed -i '/^\s*#exec-once = ags/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf - sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh - sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/Refresh.sh +# Check if quickshell is installed edit quickshell behaviour on configs +if command -v qs >/dev/null 2>&1; then + sed -i '/^\s*#exec-once = qs/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh + sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/Refresh.sh fi printf "\n%.0s" {1..1} @@ -461,7 +461,7 @@ fi printf "${INFO} - copying dotfiles ${SKY_BLUE}first${RESET} part\n" # Config directories which will ask the user whether to replace or not -DIRS="ags fastfetch kitty rofi swaync" +DIRS="fastfetch kitty rofi swaync" for DIR2 in $DIRS; do DIRPATH="$HOME/.config/$DIR2" @@ -670,6 +670,43 @@ done printf "\n%.0s" {1..1} +# quickshell (ags alternative) +# Check if quickshell is running +if pgrep -x "qs" >/dev/null; then + echo -e "${NOTE} - ${YELLOW}quickshell${RESET} is currently installed and running." + + DIRPATH_QS="$HOME/.config/quickshell" + + if [ ! -d "$DIRPATH_QS" ]; then + echo "${INFO} - quickshell config not found, copying new config." + if [ -d "config/quickshell" ]; then + cp -r "config/quickshell/" "$DIRPATH" 2>&1 | tee -a "$LOG" + fi + else + read -p "${CAT} Do you want to overwrite your existing ${YELLOW}quickshell${RESET} config? [y/N] " answer_qs + case "$answer_qs" in + [Yy]* ) + BACKUP_DIR=$(get_backup_dirname) + mv "$DIRPATH_QS" "$DIRPATH_QS-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" + echo -e "${NOTE} - Backed up quickshell to $DIRPATH_QS-backup-$BACKUP_DIR" + + cp -r "config/quickshell/" "$DIRPATH_QS" 2>&1 | tee -a "$LOG" + if [ $? -eq 0 ]; then + echo "${OK} - ${YELLOW}quickshell${RESET} overwritten successfully." + else + echo "${ERROR} - Failed to copy ${YELLOW}quickshell${RESET} config." + exit 1 + fi + ;; + * ) + echo "${NOTE} - Skipping overwrite of quickshell config." + ;; + esac + fi +fi +printf "\n%.0s" {1..1} + + # Restore automatically Animations and Monitor-Profiles # including monitors.conf and workspaces.conf HYPR_DIR="$HOME/.config/hypr" @@ -1037,4 +1074,4 @@ printf "\n%.0s" {1..1} printf "${INFO} However, it is ${MAGENTA}HIGHLY SUGGESTED${RESET} to logout and re-login or better reboot to avoid any issues" printf "\n%.0s" {1..1} printf "${SKY_BLUE}Thank you${RESET} for using ${MAGENTA}KooL's Hyprland Configuration${RESET}... ${YELLOW}ENJOY!!!${RESET}" -printf "\n%.0s" {1..3} +printf "\n%.0s" {1..3} \ No newline at end of file -- cgit v1.2.3 From eb6c637b8789edcaa4439382bad91c0fb4df8e1b Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 15 Jul 2025 19:47:15 +0900 Subject: removing ags --- config/ags/config.js | 31 -- config/ags/modules/.configuration/user_options.js | 127 ------- config/ags/modules/.miscutils/icons.js | 13 - config/ags/modules/.miscutils/mathfuncs.js | 4 - config/ags/modules/.miscutils/system.js | 54 --- .../ags/modules/.widgethacks/advancedrevealers.js | 86 ----- config/ags/modules/.widgethacks/popupwindow.js | 32 -- config/ags/modules/.widgetutils/clickthrough.js | 4 - config/ags/modules/.widgetutils/cursorhover.js | 57 --- config/ags/modules/.widgetutils/keybind.js | 25 -- config/ags/modules/overview/actions.js | 28 -- config/ags/modules/overview/main.js | 18 - config/ags/modules/overview/miscfunctions.js | 155 -------- config/ags/modules/overview/overview_hyprland.js | 423 --------------------- config/ags/modules/overview/searchbuttons.js | 163 -------- config/ags/modules/overview/searchitem.js | 65 ---- config/ags/modules/overview/windowcontent.js | 262 ------------- config/ags/user/style.css | 197 ---------- config/ags/user_options.js | 21 - config/ags/variables.js | 21 - 20 files changed, 1786 deletions(-) delete mode 100644 config/ags/config.js delete mode 100644 config/ags/modules/.configuration/user_options.js delete mode 100644 config/ags/modules/.miscutils/icons.js delete mode 100644 config/ags/modules/.miscutils/mathfuncs.js delete mode 100644 config/ags/modules/.miscutils/system.js delete mode 100644 config/ags/modules/.widgethacks/advancedrevealers.js delete mode 100644 config/ags/modules/.widgethacks/popupwindow.js delete mode 100644 config/ags/modules/.widgetutils/clickthrough.js delete mode 100644 config/ags/modules/.widgetutils/cursorhover.js delete mode 100644 config/ags/modules/.widgetutils/keybind.js delete mode 100644 config/ags/modules/overview/actions.js delete mode 100644 config/ags/modules/overview/main.js delete mode 100644 config/ags/modules/overview/miscfunctions.js delete mode 100644 config/ags/modules/overview/overview_hyprland.js delete mode 100644 config/ags/modules/overview/searchbuttons.js delete mode 100644 config/ags/modules/overview/searchitem.js delete mode 100644 config/ags/modules/overview/windowcontent.js delete mode 100644 config/ags/user/style.css delete mode 100644 config/ags/user_options.js delete mode 100644 config/ags/variables.js (limited to 'config') diff --git a/config/ags/config.js b/config/ags/config.js deleted file mode 100644 index 278bf351..00000000 --- a/config/ags/config.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -import GLib from 'gi://GLib'; -import App from 'resource:///com/github/Aylur/ags/app.js' -import userOptions from './modules/.configuration/user_options.js'; -import Overview from './modules/overview/main.js'; - -const COMPILED_STYLE_DIR = `${GLib.get_user_config_dir()}/ags/user/` - -async function applyStyle() { - - App.resetCss(); - App.applyCss(`${COMPILED_STYLE_DIR}/style.css`); - console.log('[LOG] Styles loaded') -} -applyStyle().catch(print); - -const Windows = () => [ - Overview() -]; -const CLOSE_ANIM_TIME = 210; -App.config({ - css: `${COMPILED_STYLE_DIR}/style.css`, - stackTraceOnError: true, - closeWindowDelay: { - 'sideright': CLOSE_ANIM_TIME, - 'sideleft': CLOSE_ANIM_TIME, - 'osk': CLOSE_ANIM_TIME, - }, - windows: Windows().flat(1), -}); - diff --git a/config/ags/modules/.configuration/user_options.js b/config/ags/modules/.configuration/user_options.js deleted file mode 100644 index 242c0575..00000000 --- a/config/ags/modules/.configuration/user_options.js +++ /dev/null @@ -1,127 +0,0 @@ - -import userOverrides from '../../user_options.js'; - -// Defaults -let configOptions = { - // General stuff - 'ai': { - 'defaultGPTProvider': "openai", - 'defaultTemperature': 0.9, - 'enhancements': true, - 'useHistory': true, - 'writingCursor': " ...", // Warning: Using weird characters can mess up Markdown rendering - }, - 'animations': { - 'choreographyDelay': 35, - 'durationSmall': 110, - 'durationLarge': 180, - }, - 'appearance': { - 'keyboardUseFlag': false, // Use flag emoji instead of abbreviation letters - }, - 'apps': { - 'imageViewer': "loupe", - 'terminal': "foot", // This is only for shell actions - }, - 'battery': { - 'low': 20, - 'critical': 10, - }, - 'music': { - 'preferredPlayer': "plasma-browser-integration", - }, - 'onScreenKeyboard': { - 'layout': "qwerty_full", // See modules/onscreenkeyboard/onscreenkeyboard.js for available layouts - }, - 'overview': { - 'scale': 0.18, // Relative to screen size - 'numOfRows': 2, - 'numOfCols': 5, - 'wsNumScale': 0.09, - 'wsNumMarginScale': 0.07, - }, - 'sidebar': { - 'imageColumns': 2, - 'imageBooruCount': 20, - 'imageAllowNsfw': false, - }, - 'search': { - 'engineBaseUrl': "https://www.google.com/search?q=", - 'excludedSites': [], //add site to exclude from result. eg: "quora.com" - }, - 'time': { - // See https://docs.gtk.org/glib/method.DateTime.format.html - // Here's the 12h format: "%I:%M%P" - // For seconds, add "%S" and set interval to 1000 - 'format': "%H:%M", - 'interval': 5000, - 'dateFormatLong': "%A, %d/%m", // On bar - 'dateInterval': 5000, - 'dateFormat': "%d/%m", // On notif time - }, - 'weather': { - 'city': "", - }, - 'workspaces': { - 'shown': 10, - }, - // Longer stuff - 'icons': { - substitutions: { - 'codium-url-handler': "vscodium", - 'codium': "vscodium", - 'code-url-handler': "visual-studio-code", - 'Code': "visual-studio-code", - 'GitHub Desktop': "github-desktop", - 'Minecraft* 1.20.1': "minecraft", - 'gnome-tweaks': "org.gnome.tweaks", - 'pavucontrol-qt': "pavucontrol", - 'eu.betterbird.Betterbird' : "thunderbird", - 'thunderbird-esr': "thunderbird", - 'wps': "wps-office2019-kprometheus", - 'wpsoffice': "wps-office2019-kprometheus", - 'firefox-esr': "firefox", - 'soffice' : "libreoffice", - '': "image-missing", - } - }, - 'keybinds': { - // Format: Mod1+Mod2+key. CaSe SeNsItIvE! - // Modifiers: Shift Ctrl Alt Hyper Meta - // See https://docs.gtk.org/gdk3/index.html#constants for the other keys (they are listed as KEY_key) - 'overview': { - 'altMoveLeft': "Ctrl+b", - 'altMoveRight': "Ctrl+f", - 'deleteToEnd': "Ctrl+k", - }, - 'sidebar': { - 'apis': { - 'nextTab': "Page_Down", - 'prevTab': "Page_Up", - }, - 'options': { // Right sidebar - 'nextTab': "Page_Down", - 'prevTab': "Page_Up", - }, - 'pin': "Ctrl+p", - 'cycleTab': "Ctrl+Tab", - 'nextTab': "Ctrl+Page_Down", - 'prevTab': "Ctrl+Page_Up", - }, - }, -} - -// Override defaults with user's options -function overrideConfigRecursive(userOverrides, configOptions = {}) { - for (const [key, value] of Object.entries(userOverrides)) { - if (typeof value === 'object') { - overrideConfigRecursive(value, configOptions[key]); - } else { - configOptions[key] = value; - } - } -} -overrideConfigRecursive(userOverrides, configOptions); - -globalThis['userOptions'] = configOptions; -export default configOptions; \ No newline at end of file diff --git a/config/ags/modules/.miscutils/icons.js b/config/ags/modules/.miscutils/icons.js deleted file mode 100644 index fb1e20da..00000000 --- a/config/ags/modules/.miscutils/icons.js +++ /dev/null @@ -1,13 +0,0 @@ -const { Gtk } = imports.gi; - -export function iconExists(iconName) { - let iconTheme = Gtk.IconTheme.get_default(); - return iconTheme.has_icon(iconName); -} - -export function substitute(str) { - if(userOptions.icons.substitutions[str]) return userOptions.icons.substitutions[str]; - - if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case - return str; -} \ No newline at end of file diff --git a/config/ags/modules/.miscutils/mathfuncs.js b/config/ags/modules/.miscutils/mathfuncs.js deleted file mode 100644 index ba1c0b59..00000000 --- a/config/ags/modules/.miscutils/mathfuncs.js +++ /dev/null @@ -1,4 +0,0 @@ - -export function clamp(x, min, max) { - return Math.min(Math.max(x, min), max); -} \ No newline at end of file diff --git a/config/ags/modules/.miscutils/system.js b/config/ags/modules/.miscutils/system.js deleted file mode 100644 index ef7d15c3..00000000 --- a/config/ags/modules/.miscutils/system.js +++ /dev/null @@ -1,54 +0,0 @@ -const { GLib } = imports.gi; -import Variable from 'resource:///com/github/Aylur/ags/variable.js'; -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; -const { execAsync, exec } = Utils; - -export const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2 | sed "s/\\"//g"'`).trim(); -export const isDebianDistro = (distroID == 'linuxmint' || distroID == 'ubuntu' || distroID == 'debian' || distroID == 'zorin' || distroID == 'popos' || distroID == 'raspbian' || distroID == 'kali'); -export const isArchDistro = (distroID == 'arch' || distroID == 'endeavouros' || distroID == 'cachyos'); -export const hasFlatpak = !!exec(`bash -c 'command -v flatpak'`); - -const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/colormode.txt`; -const colorMode = Utils.exec('bash -c "sed -n \'1p\' $HOME/.cache/ags/user/colormode.txt"'); -export let darkMode = Variable(!(Utils.readFile(LIGHTDARK_FILE_LOCATION).split('\n')[0].trim() == 'light')); -export const hasPlasmaIntegration = !!Utils.exec('bash -c "command -v plasma-browser-integration-host"'); - -export const getDistroIcon = () => { - // Arches - if(distroID == 'arch') return 'arch-symbolic'; - if(distroID == 'endeavouros') return 'endeavouros-symbolic'; - if(distroID == 'cachyos') return 'cachyos-symbolic'; - // Funny flake - if(distroID == 'nixos') return 'nixos-symbolic'; - // Cool thing - if(distroID == 'fedora') return 'fedora-symbolic'; - // Debians - if(distroID == 'linuxmint') return 'ubuntu-symbolic'; - if(distroID == 'ubuntu') return 'ubuntu-symbolic'; - if(distroID == 'debian') return 'debian-symbolic'; - if(distroID == 'zorin') return 'ubuntu-symbolic'; - if(distroID == 'popos') return 'ubuntu-symbolic'; - if(distroID == 'raspbian') return 'debian-symbolic'; - if(distroID == 'kali') return 'debian-symbolic'; - return 'linux-symbolic'; -} - -export const getDistroName = () => { - // Arches - if(distroID == 'arch') return 'Arch Linux'; - if(distroID == 'endeavouros') return 'EndeavourOS'; - if(distroID == 'cachyos') return 'CachyOS'; - // Funny flake - if(distroID == 'nixos') return 'NixOS'; - // Cool thing - if(distroID == 'fedora') return 'Fedora'; - // Debians - if(distroID == 'linuxmint') return 'Linux Mint'; - if(distroID == 'ubuntu') return 'Ubuntu'; - if(distroID == 'debian') return 'Debian'; - if(distroID == 'zorin') return 'Zorin'; - if(distroID == 'popos') return 'Pop!_OS'; - if(distroID == 'raspbian') return 'Raspbian'; - if(distroID == 'kali') return 'Kali Linux'; - return 'Linux'; -} diff --git a/config/ags/modules/.widgethacks/advancedrevealers.js b/config/ags/modules/.widgethacks/advancedrevealers.js deleted file mode 100644 index 3f127931..00000000 --- a/config/ags/modules/.widgethacks/advancedrevealers.js +++ /dev/null @@ -1,86 +0,0 @@ -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; - -const { Revealer, Scrollable } = Widget; - -export const MarginRevealer = ({ - transition = 'slide_down', - child, - revealChild, - showClass = 'element-show', // These are for animation curve, they don't really hide - hideClass = 'element-hide', // Don't put margins in these classes! - extraSetup = () => { }, - ...rest -}) => { - const widget = Scrollable({ - ...rest, - attribute: { - 'revealChild': true, // It'll be set to false after init if it's supposed to hide - 'transition': transition, - 'show': () => { - if (widget.attribute.revealChild) return; - widget.hscroll = 'never'; - widget.vscroll = 'never'; - child.toggleClassName(hideClass, false); - child.toggleClassName(showClass, true); - widget.attribute.revealChild = true; - child.css = 'margin: 0px;'; - }, - 'hide': () => { - if (!widget.attribute.revealChild) return; - child.toggleClassName(hideClass, true); - child.toggleClassName(showClass, false); - widget.attribute.revealChild = false; - if (widget.attribute.transition == 'slide_left') - child.css = `margin-right: -${child.get_allocated_width()}px;`; - else if (widget.attribute.transition == 'slide_right') - child.css = `margin-left: -${child.get_allocated_width()}px;`; - else if (widget.attribute.transition == 'slide_up') - child.css = `margin-bottom: -${child.get_allocated_height()}px;`; - else if (widget.attribute.transition == 'slide_down') - child.css = `margin-top: -${child.get_allocated_height()}px;`; - }, - 'toggle': () => { - if (widget.attribute.revealChild) widget.attribute.hide(); - else widget.attribute.show(); - }, - }, - child: child, - hscroll: `${revealChild ? 'never' : 'always'}`, - vscroll: `${revealChild ? 'never' : 'always'}`, - setup: (self) => { - extraSetup(self); - } - }); - child.toggleClassName(`${revealChild ? showClass : hideClass}`, true); - return widget; -} - -// TODO: Allow reveal update. Currently this just helps at declaration -export const DoubleRevealer = ({ - transition1 = 'slide_right', - transition2 = 'slide_left', - duration1 = 150, - duration2 = 150, - child, - revealChild, - ...rest -}) => { - const r2 = Revealer({ - transition: transition2, - transitionDuration: duration2, - revealChild: revealChild, - child: child, - }); - const r1 = Revealer({ - transition: transition1, - transitionDuration: duration1, - revealChild: revealChild, - child: r2, - ...rest, - }) - r1.toggleRevealChild = (value) => { - r1.revealChild = value; - r2.revealChild = value; - } - return r1; -} diff --git a/config/ags/modules/.widgethacks/popupwindow.js b/config/ags/modules/.widgethacks/popupwindow.js deleted file mode 100644 index 26dad59c..00000000 --- a/config/ags/modules/.widgethacks/popupwindow.js +++ /dev/null @@ -1,32 +0,0 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -const { Box, Window } = Widget; - - -export default ({ - name, - child, - showClassName = "", - hideClassName = "", - ...props -}) => { - return Window({ - name, - visible: false, - layer: 'overlay', - ...props, - - child: Box({ - setup: (self) => { - self.hook(App, (self, currentName, visible) => { - if (currentName === name) { - self.toggleClassName(hideClassName, !visible); - } - }).keybind("Escape", () => App.closeWindow(name)) - if (showClassName !== "" && hideClassName !== "") - self.className = `${showClassName} ${hideClassName}`; - }, - child: child, - }), - }); -} \ No newline at end of file diff --git a/config/ags/modules/.widgetutils/clickthrough.js b/config/ags/modules/.widgetutils/clickthrough.js deleted file mode 100644 index 505f1412..00000000 --- a/config/ags/modules/.widgetutils/clickthrough.js +++ /dev/null @@ -1,4 +0,0 @@ -import Cairo from 'gi://cairo?version=1.0'; - -export const dummyRegion = new Cairo.Region(); -export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); \ No newline at end of file diff --git a/config/ags/modules/.widgetutils/cursorhover.js b/config/ags/modules/.widgetutils/cursorhover.js deleted file mode 100644 index 89be913b..00000000 --- a/config/ags/modules/.widgetutils/cursorhover.js +++ /dev/null @@ -1,57 +0,0 @@ -const { Gdk } = imports.gi; - -export function setupCursorHover(button) { // Hand pointing cursor on hover - const display = Gdk.Display.get_default(); - button.connect('enter-notify-event', () => { - const cursor = Gdk.Cursor.new_from_name(display, 'pointer'); - button.get_window().set_cursor(cursor); - }); - - button.connect('leave-notify-event', () => { - const cursor = Gdk.Cursor.new_from_name(display, 'default'); - button.get_window().set_cursor(cursor); - }); - -} - -export function setupCursorHoverAim(button) { // Crosshair cursor on hover - button.connect('enter-notify-event', () => { - const display = Gdk.Display.get_default(); - const cursor = Gdk.Cursor.new_from_name(display, 'crosshair'); - button.get_window().set_cursor(cursor); - }); - - button.connect('leave-notify-event', () => { - const display = Gdk.Display.get_default(); - const cursor = Gdk.Cursor.new_from_name(display, 'default'); - button.get_window().set_cursor(cursor); - }); -} - -export function setupCursorHoverGrab(button) { // Hand ready to grab on hover - button.connect('enter-notify-event', () => { - const display = Gdk.Display.get_default(); - const cursor = Gdk.Cursor.new_from_name(display, 'grab'); - button.get_window().set_cursor(cursor); - }); - - button.connect('leave-notify-event', () => { - const display = Gdk.Display.get_default(); - const cursor = Gdk.Cursor.new_from_name(display, 'default'); - button.get_window().set_cursor(cursor); - }); -} - -export function setupCursorHoverInfo(button) { // "?" mark cursor on hover - const display = Gdk.Display.get_default(); - button.connect('enter-notify-event', () => { - const cursor = Gdk.Cursor.new_from_name(display, 'help'); - button.get_window().set_cursor(cursor); - }); - - button.connect('leave-notify-event', () => { - const cursor = Gdk.Cursor.new_from_name(display, 'default'); - button.get_window().set_cursor(cursor); - }); -} - diff --git a/config/ags/modules/.widgetutils/keybind.js b/config/ags/modules/.widgetutils/keybind.js deleted file mode 100644 index eda7877b..00000000 --- a/config/ags/modules/.widgetutils/keybind.js +++ /dev/null @@ -1,25 +0,0 @@ -const { Gdk } = imports.gi; - -const MODS = { - 'Shift': Gdk.ModifierType.SHIFT_MASK, - 'Ctrl': Gdk.ModifierType.CONTROL_MASK, - 'Alt': Gdk.ModifierType.ALT_MASK, - 'Hyper': Gdk.ModifierType.HYPER_MASK, - 'Meta': Gdk.ModifierType.META_MASK -} - -export const checkKeybind = (event, keybind) => { - const pressedModMask = event.get_state()[1]; - const pressedKey = event.get_keyval()[1]; - const keys = keybind.split('+'); - for (let i = 0; i < keys.length; i++) { - if (keys[i] in MODS) { - if (!(pressedModMask & MODS[keys[i]])) { - return false; - } - } else if (pressedKey !== Gdk[`KEY_${keys[i]}`]) { - return false; - } - } - return true; -} diff --git a/config/ags/modules/overview/actions.js b/config/ags/modules/overview/actions.js deleted file mode 100644 index 766cf454..00000000 --- a/config/ags/modules/overview/actions.js +++ /dev/null @@ -1,28 +0,0 @@ -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; - -function moveClientToWorkspace(address, workspace) { - Utils.execAsync(['bash', '-c', `hyprctl dispatch movetoworkspacesilent ${workspace},address:${address} &`]); -} - -export function dumpToWorkspace(from, to) { - if (from == to) return; - Hyprland.clients.forEach(client => { - if (client.workspace.id == from) { - moveClientToWorkspace(client.address, to); - } - }); -} - -export function swapWorkspace(workspaceA, workspaceB) { - if (workspaceA == workspaceB) return; - const clientsA = []; - const clientsB = []; - Hyprland.clients.forEach(client => { - if (client.workspace.id == workspaceA) clientsA.push(client.address); - if (client.workspace.id == workspaceB) clientsB.push(client.address); - }); - - clientsA.forEach((address) => moveClientToWorkspace(address, workspaceB)); - clientsB.forEach((address) => moveClientToWorkspace(address, workspaceA)); -} \ No newline at end of file diff --git a/config/ags/modules/overview/main.js b/config/ags/modules/overview/main.js deleted file mode 100644 index 1f5348d9..00000000 --- a/config/ags/modules/overview/main.js +++ /dev/null @@ -1,18 +0,0 @@ -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import { SearchAndWindows } from "./windowcontent.js"; -import PopupWindow from '../.widgethacks/popupwindow.js'; - -export default (id = '') => PopupWindow({ - name: `overview${id}`, - exclusivity: 'ignore', - keymode: 'exclusive', - visible: false, - // anchor: ['middle'], - layer: 'overlay', - child: Widget.Box({ - vertical: true, - children: [ - SearchAndWindows(), - ] - }), -}) diff --git a/config/ags/modules/overview/miscfunctions.js b/config/ags/modules/overview/miscfunctions.js deleted file mode 100644 index 187ee6ec..00000000 --- a/config/ags/modules/overview/miscfunctions.js +++ /dev/null @@ -1,155 +0,0 @@ -const { Gio, GLib } = imports.gi; -import App from 'resource:///com/github/Aylur/ags/app.js'; -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; -const { execAsync, exec } = Utils; -// import Todo from "../../services/todo.js"; -import { darkMode } from '../.miscutils/system.js'; - -export function hasUnterminatedBackslash(inputString) { - // Use a regular expression to match a trailing odd number of backslashes - const regex = /\\+$/; - return regex.test(inputString); -} - -export function launchCustomCommand(command) { - const args = command.toLowerCase().split(' '); - if (args[0] == '>raw') { // Mouse raw input - Utils.execAsync('hyprctl -j getoption input:accel_profile') - .then((output) => { - const value = JSON.parse(output)["str"].trim(); - if (value != "[[EMPTY]]" && value != "") { - execAsync(['bash', '-c', `hyprctl keyword input:accel_profile '[[EMPTY]]'`]).catch(print); - } - else { - execAsync(['bash', '-c', `hyprctl keyword input:accel_profile flat`]).catch(print); - } - }) - } - else if (args[0] == '>img') { // Change wallpaper - execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchwall.sh`, `&`]).catch(print); - } - else if (args[0] == '>color') { // Generate colorscheme from color picker - execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh --pick`, `&`]).catch(print); - } - else if (args[0] == '>light') { // Light mode - darkMode.value = false; - execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/light/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) - .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) - .catch(print); - } - else if (args[0] == '>dark') { // Dark mode - darkMode.value = true; - execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/dark/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) - .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) - .catch(print); - } - else if (args[0] == '>badapple') { // Black and white - execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "3s/.*/monochrome/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) - .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) - .catch(print); - } - else if (args[0] == '>material') { // Use material colors - execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "material" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print) - .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print)) - .catch(print); - } - else if (args[0] == '>pywal') { // Use Pywal (ik it looks shit but I'm not removing) - execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "pywal" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print) - .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print)) - .catch(print); - } - else if (args[0] == '>todo') { // Todo - Todo.add(args.slice(1).join(' ')); - } - else if (args[0] == '>shutdown') { // Shut down - execAsync([`bash`, `-c`, `systemctl poweroff || loginctl poweroff`]).catch(print); - } - else if (args[0] == '>reboot') { // Reboot - execAsync([`bash`, `-c`, `systemctl reboot || loginctl reboot`]).catch(print); - } - else if (args[0] == '>sleep') { // Sleep - execAsync([`bash`, `-c`, `systemctl suspend || loginctl suspend`]).catch(print); - } - else if (args[0] == '>logout') { // Log out - execAsync([`bash`, `-c`, `pkill Hyprland || pkill sway`]).catch(print); - } -} - -export function execAndClose(command, terminal) { - App.closeWindow('overview'); - if (terminal) { - execAsync([`bash`, `-c`, `${userOptions.apps.terminal} fish -C "${command}"`, `&`]).catch(print); - } - else - execAsync(command).catch(print); -} - -export function couldBeMath(str) { - const regex = /^[0-9.+*/-]/; - return regex.test(str); -} - -export function expandTilde(path) { - if (path.startsWith('~')) { - return GLib.get_home_dir() + path.slice(1); - } else { - return path; - } -} - -function getFileIcon(fileInfo) { - let icon = fileInfo.get_icon(); - if (icon) { - // Get the icon's name - return icon.get_names()[0]; - } else { - // Default icon for files - return 'text-x-generic'; - } -} - -export function ls({ path = '~', silent = false }) { - let contents = []; - try { - let expandedPath = expandTilde(path); - if (expandedPath.endsWith('/')) - expandedPath = expandedPath.slice(0, -1); - let folder = Gio.File.new_for_path(expandedPath); - - let enumerator = folder.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); - let fileInfo; - while ((fileInfo = enumerator.next_file(null)) !== null) { - let fileName = fileInfo.get_display_name(); - let fileType = fileInfo.get_file_type(); - - let item = { - parentPath: expandedPath, - name: fileName, - type: fileType === Gio.FileType.DIRECTORY ? 'folder' : 'file', - icon: getFileIcon(fileInfo), - }; - - // Add file extension for files - if (fileType === Gio.FileType.REGULAR) { - let fileExtension = fileName.split('.').pop(); - item.type = `${fileExtension}`; - } - - contents.push(item); - contents.sort((a, b) => { - const aIsFolder = a.type.startsWith('folder'); - const bIsFolder = b.type.startsWith('folder'); - if (aIsFolder && !bIsFolder) { - return -1; - } else if (!aIsFolder && bIsFolder) { - return 1; - } else { - return a.name.localeCompare(b.name); // Sort alphabetically within folders and files - } - }); - } - } catch (e) { - if (!silent) console.log(e); - } - return contents; -} diff --git a/config/ags/modules/overview/overview_hyprland.js b/config/ags/modules/overview/overview_hyprland.js deleted file mode 100644 index 034d6c81..00000000 --- a/config/ags/modules/overview/overview_hyprland.js +++ /dev/null @@ -1,423 +0,0 @@ -// TODO -// - Make client destroy/create not destroy and recreate the whole thing -// - Active ws hook optimization: only update when moving to next group -// -const { Gdk, Gtk } = imports.gi; -const { Gravity } = imports.gi.Gdk; -import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../variables.js'; -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Variable from 'resource:///com/github/Aylur/ags/variable.js'; -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; - -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; -const { execAsync, exec } = Utils; -import { setupCursorHoverGrab } from '../.widgetutils/cursorhover.js'; -import { dumpToWorkspace, swapWorkspace } from "./actions.js"; -import { substitute } from "../.miscutils/icons.js"; - -const NUM_OF_WORKSPACES_SHOWN = userOptions.overview.numOfCols * userOptions.overview.numOfRows; -const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; -const POPUP_CLOSE_TIME = 100; // ms - -const overviewTick = Variable(false); - -export default () => { - const clientMap = new Map(); - let workspaceGroup = 0; - const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({ - label: `${label}`, - setup: (menuItem) => { - let submenu = new Gtk.Menu(); - submenu.className = 'menu'; - - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - const startWorkspace = offset + 1; - const endWorkspace = startWorkspace + NUM_OF_WORKSPACES_SHOWN - 1; - for (let i = startWorkspace; i <= endWorkspace; i++) { - let button = new Gtk.MenuItem({ - label: `Workspace ${i}` - }); - button.connect("activate", () => { - // execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print); - actionFunc(thisWorkspace, i); - overviewTick.setValue(!overviewTick.value); - }); - submenu.append(button); - } - menuItem.set_reserve_indicator(true); - menuItem.set_submenu(submenu); - } - }) - - const Window = ({ address, at: [x, y], size: [w, h], workspace: { id, name }, class: c, title, xwayland }, screenCoords) => { - const revealInfoCondition = (Math.min(w, h) * userOptions.overview.scale > 70); - if (w <= 0 || h <= 0 || (c === '' && title === '') || c.endsWith('-dropterm')) return null; - // Non-primary monitors - if (screenCoords.x != 0) x -= screenCoords.x; - if (screenCoords.y != 0) y -= screenCoords.y; - // Other offscreen adjustments - if (x + w <= 0) x += (Math.floor(x / SCREEN_WIDTH) * SCREEN_WIDTH); - else if (x < 0) { w = x + w; x = 0; } - if (y + h <= 0) x += (Math.floor(y / SCREEN_HEIGHT) * SCREEN_HEIGHT); - else if (y < 0) { h = y + h; y = 0; } - // Truncate if offscreen - if (x + w > SCREEN_WIDTH) w = SCREEN_WIDTH - x; - if (y + h > SCREEN_HEIGHT) h = SCREEN_HEIGHT - y; - - const appIcon = Widget.Icon({ - icon: substitute(c), - size: Math.min(w, h) * userOptions.overview.scale / 2.5, - }); - return Widget.Button({ - attribute: { - address, x, y, w, h, ws: id, - updateIconSize: (self) => { - appIcon.size = Math.min(self.attribute.w, self.attribute.h) * userOptions.overview.scale / 2.5; - }, - }, - className: 'overview-tasks-window', - hpack: 'start', - vpack: 'start', - css: ` - margin-left: ${Math.round(x * userOptions.overview.scale)}px; - margin-top: ${Math.round(y * userOptions.overview.scale)}px; - margin-right: -${Math.round((x + w) * userOptions.overview.scale)}px; - margin-bottom: -${Math.round((y + h) * userOptions.overview.scale)}px; - `, - onClicked: (self) => { - App.closeWindow('overview'); - Utils.timeout(POPUP_CLOSE_TIME, () => Hyprland.messageAsync(`dispatch focuswindow address:${address}`)); - }, - onMiddleClickRelease: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`), - onSecondaryClick: (button) => { - button.toggleClassName('overview-tasks-window-selected', true); - const menu = Widget.Menu({ - className: 'menu', - children: [ - Widget.MenuItem({ - child: Widget.Label({ - xalign: 0, - label: "Close (Middle-click)", - }), - onActivate: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`), - }), - ContextMenuWorkspaceArray({ - label: "Dump windows to workspace", - actionFunc: dumpToWorkspace, - thisWorkspace: Number(id) - }), - ContextMenuWorkspaceArray({ - label: "Swap windows with workspace", - actionFunc: swapWorkspace, - thisWorkspace: Number(id) - }), - ], - }); - menu.connect("deactivate", () => { - button.toggleClassName('overview-tasks-window-selected', false); - }) - menu.connect("selection-done", () => { - button.toggleClassName('overview-tasks-window-selected', false); - }) - menu.popup_at_widget(button.get_parent(), Gravity.SOUTH, Gravity.NORTH, null); // Show menu below the button - button.connect("destroy", () => menu.destroy()); - }, - child: Widget.Box({ - homogeneous: true, - child: Widget.Box({ - vertical: true, - vpack: 'center', - className: 'spacing-v-5', - children: [ - appIcon, - // TODO: Add xwayland tag instead of just having italics - Widget.Revealer({ - transition: 'slide_down', - revealChild: revealInfoCondition, - child: Widget.Label({ - maxWidthChars: 10, // Doesn't matter what number - truncate: 'end', - className: `${xwayland ? 'txt txt-italic' : 'txt'}`, - css: ` - font-size: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale / 14.6}px; - margin: 0px ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale / 10}px; - `, - // If the title is too short, include the class - label: (title.length <= 1 ? `${c}: ${title}` : title), - }) - }) - ] - }) - }), - tooltipText: `${c}: ${title}`, - setup: (button) => { - setupCursorHoverGrab(button); - - button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE); - button.drag_source_set_icon_name(substitute(c)); - // button.drag_source_set_icon_gicon(icon); - - button.connect('drag-begin', (button) => { // On drag start, add the dragging class - button.toggleClassName('overview-tasks-window-dragging', true); - }); - button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address - data.set_text(address, address.length); - button.toggleClassName('overview-tasks-window-dragging', false); - }); - }, - }); - } - - const Workspace = (index) => { - // const fixed = Widget.Fixed({ - // attribute: { - // put: (widget, x, y) => { - // fixed.put(widget, x, y); - // }, - // move: (widget, x, y) => { - // fixed.move(widget, x, y); - // }, - // } - // }); - const fixed = Widget.Box({ - attribute: { - put: (widget, x, y) => { - if (!widget.attribute) return; - // Note: x and y are already multiplied by userOptions.overview.scale - const newCss = ` - margin-left: ${Math.round(x)}px; - margin-top: ${Math.round(y)}px; - margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px; - margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px; - `; - widget.css = newCss; - fixed.pack_start(widget, false, false, 0); - }, - move: (widget, x, y) => { - if (!widget) return; - if (!widget.attribute) return; - // Note: x and y are already multiplied by userOptions.overview.scale - const newCss = ` - margin-left: ${Math.round(x)}px; - margin-top: ${Math.round(y)}px; - margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px; - margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px; - `; - widget.css = newCss; - }, - } - }) - const WorkspaceNumber = ({ index, ...rest }) => Widget.Label({ - className: 'overview-tasks-workspace-number', - label: `${index}`, - css: ` - margin: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale * userOptions.overview.wsNumMarginScale}px; - font-size: ${SCREEN_HEIGHT * userOptions.overview.scale * userOptions.overview.wsNumScale}px; - `, - setup: (self) => self.hook(Hyprland.active.workspace, (self) => { - // Update when going to new ws group - const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN); - self.label = `${currentGroup * NUM_OF_WORKSPACES_SHOWN + index}`; - }), - ...rest, - }) - const widget = Widget.Box({ - className: 'overview-tasks-workspace', - vpack: 'center', - css: ` - min-width: ${SCREEN_WIDTH * userOptions.overview.scale}px; - min-height: ${SCREEN_HEIGHT * userOptions.overview.scale}px; - `, - children: [Widget.EventBox({ - hexpand: true, - vexpand: true, - onPrimaryClick: () => { - App.closeWindow('overview'); - Utils.timeout(POPUP_CLOSE_TIME, () => Hyprland.messageAsync(`dispatch workspace ${index}`)); - }, - setup: (eventbox) => { - eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); - eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => { - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - Hyprland.messageAsync(`dispatch movetoworkspacesilent ${index + offset},address:${data.get_text()}`) - overviewTick.setValue(!overviewTick.value); - }); - }, - child: Widget.Overlay({ - child: Widget.Box({}), - overlays: [ - WorkspaceNumber({ index: index, hpack: 'start', vpack: 'start' }), - fixed - ] - }), - })], - }); - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - fixed.attribute.put(WorkspaceNumber(offset + index), 0, 0); - widget.clear = () => { - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - clientMap.forEach((client, address) => { - if (!client) return; - if ((client.attribute.ws <= offset || client.attribute.ws > offset + NUM_OF_WORKSPACES_SHOWN) || - (client.attribute.ws == offset + index)) { - client.destroy(); - client = null; - clientMap.delete(address); - } - }); - } - widget.set = (clientJson, screenCoords) => { - let c = clientMap.get(clientJson.address); - if (c) { - if (c.attribute?.ws !== clientJson.workspace.id) { - c.destroy(); - c = null; - clientMap.delete(clientJson.address); - } - else if (c) { - c.attribute.w = clientJson.size[0]; - c.attribute.h = clientJson.size[1]; - c.attribute.updateIconSize(c); - fixed.attribute.move(c, - Math.max(0, clientJson.at[0] * userOptions.overview.scale), - Math.max(0, clientJson.at[1] * userOptions.overview.scale) - ); - return; - } - } - const newWindow = Window(clientJson, screenCoords); - if (newWindow === null) return; - // clientMap.set(clientJson.address, newWindow); - fixed.attribute.put(newWindow, - Math.max(0, newWindow.attribute.x * userOptions.overview.scale), - Math.max(0, newWindow.attribute.y * userOptions.overview.scale) - ); - clientMap.set(clientJson.address, newWindow); - }; - widget.unset = (clientAddress) => { - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - let c = clientMap.get(clientAddress); - if (!c) return; - c.destroy(); - c = null; - clientMap.delete(clientAddress); - }; - widget.show = () => { - fixed.show_all(); - } - return widget; - }; - - const arr = (s, n) => { - const array = []; - for (let i = 0; i < n; i++) - array.push(s + i); - - return array; - }; - - const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({ - children: arr(startWorkspace, workspaces).map(Workspace), - attribute: { - monitorMap: [], - getMonitorMap: (box) => { - execAsync('hyprctl -j monitors').then(monitors => { - box.attribute.monitorMap = JSON.parse(monitors).reduce((acc, item) => { - acc[item.id] = { x: item.x, y: item.y }; - return acc; - }, {}); - }); - }, - update: (box) => { - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - if (!App.getWindow(windowName).visible) return; - Hyprland.messageAsync('j/clients').then(clients => { - const allClients = JSON.parse(clients); - const kids = box.get_children(); - kids.forEach(kid => kid.clear()); - for (let i = 0; i < allClients.length; i++) { - const client = allClients[i]; - const childID = client.workspace.id - (offset + startWorkspace); - if (offset + startWorkspace <= client.workspace.id && - client.workspace.id <= offset + startWorkspace + workspaces) { - const screenCoords = box.attribute.monitorMap[client.monitor]; - if (kids[childID]) { - kids[childID].set(client, screenCoords); - } - continue; - } - } - kids.forEach(kid => kid.show()); - }).catch(print); - }, - updateWorkspace: (box, id) => { - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - if (!( // Not in range, ignore - offset + startWorkspace <= id && - id <= offset + startWorkspace + workspaces - )) return; - // if (!App.getWindow(windowName).visible) return; - Hyprland.messageAsync('j/clients').then(clients => { - const allClients = JSON.parse(clients); - const kids = box.get_children(); - for (let i = 0; i < allClients.length; i++) { - const client = allClients[i]; - if (client.workspace.id != id) continue; - const screenCoords = box.attribute.monitorMap[client.monitor]; - kids[id - (offset + startWorkspace)]?.set(client, screenCoords); - } - kids[id - (offset + startWorkspace)]?.show(); - }).catch(print); - }, - }, - setup: (box) => { - box.attribute.getMonitorMap(box); - box - .hook(overviewTick, (box) => box.attribute.update(box)) - .hook(Hyprland, (box, clientAddress) => { - const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; - const kids = box.get_children(); - const client = Hyprland.getClient(clientAddress); - if (!client) return; - const id = client.workspace.id; - - box.attribute.updateWorkspace(box, id); - kids[id - (offset + startWorkspace)]?.unset(clientAddress); - }, 'client-removed') - .hook(Hyprland, (box, clientAddress) => { - const client = Hyprland.getClient(clientAddress); - if (!client) return; - box.attribute.updateWorkspace(box, client.workspace.id); - }, 'client-added') - .hook(Hyprland.active.workspace, (box) => { - // Full update when going to new ws group - const previousGroup = box.attribute.workspaceGroup; - const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN); - if (currentGroup !== previousGroup) { - box.attribute.update(box); - box.attribute.workspaceGroup = currentGroup; - } - }) - .hook(App, (box, name, visible) => { // Update on open - if (name == 'overview' && visible) box.attribute.update(box); - }) - }, - }); - - return Widget.Revealer({ - revealChild: true, - transition: 'slide_down', - transitionDuration: userOptions.animations.durationLarge, - child: Widget.Box({ - vertical: true, - className: 'overview-tasks', - children: Array.from({ length: userOptions.overview.numOfRows }, (_, index) => - OverviewRow({ - startWorkspace: 1 + index * userOptions.overview.numOfCols, - workspaces: userOptions.overview.numOfCols, - }) - ) - }), - }); -} \ No newline at end of file diff --git a/config/ags/modules/overview/searchbuttons.js b/config/ags/modules/overview/searchbuttons.js deleted file mode 100644 index f5892f31..00000000 --- a/config/ags/modules/overview/searchbuttons.js +++ /dev/null @@ -1,163 +0,0 @@ -const { Gtk } = imports.gi; -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; -const { execAsync, exec } = Utils; -import { searchItem } from './searchitem.js'; -import { execAndClose, couldBeMath, launchCustomCommand } from './miscfunctions.js'; - -export const DirectoryButton = ({ parentPath, name, type, icon }) => { - const actionText = Widget.Revealer({ - revealChild: false, - transition: "crossfade", - transitionDuration: userOptions.animations.durationLarge, - child: Widget.Label({ - className: 'overview-search-results-txt txt txt-small txt-action', - label: 'Open', - }) - }); - const actionTextRevealer = Widget.Revealer({ - revealChild: false, - transition: "slide_left", - transitionDuration: userOptions.animations.durationSmall, - child: actionText, - }); - return Widget.Button({ - className: 'overview-search-result-btn', - onClicked: () => { - App.closeWindow('overview'); - execAsync(['bash', '-c', `xdg-open '${parentPath}/${name}'`, `&`]).catch(print); - }, - child: Widget.Box({ - children: [ - Widget.Box({ - vertical: false, - children: [ - Widget.Box({ - className: 'overview-search-results-icon', - homogeneous: true, - child: Widget.Icon({ - icon: icon, - }), - }), - Widget.Label({ - className: 'overview-search-results-txt txt txt-norm', - label: name, - }), - Widget.Box({ hexpand: true }), - actionTextRevealer, - ] - }) - ] - }), - setup: (self) => self - .on('focus-in-event', (button) => { - actionText.revealChild = true; - actionTextRevealer.revealChild = true; - }) - .on('focus-out-event', (button) => { - actionText.revealChild = false; - actionTextRevealer.revealChild = false; - }) - , - }) -} - -export const CalculationResultButton = ({ result, text }) => searchItem({ - materialIconName: '󱖦 ', - name: `Math result`, - actionName: "Copy", - content: `${result}`, - onActivate: () => { - App.closeWindow('overview'); - execAsync(['wl-copy', `${result}`]).catch(print); - }, -}); - -export const DesktopEntryButton = (app) => { - const actionText = Widget.Revealer({ - revealChild: false, - transition: "crossfade", - transitionDuration: userOptions.animations.durationLarge, - child: Widget.Label({ - className: 'overview-search-results-txt txt txt-small txt-action', - label: 'Launch', - }) - }); - const actionTextRevealer = Widget.Revealer({ - revealChild: false, - transition: "slide_left", - transitionDuration: userOptions.animations.durationSmall, - child: actionText, - }); - return Widget.Button({ - className: 'overview-search-result-btn', - onClicked: () => { - App.closeWindow('overview'); - app.launch(); - }, - child: Widget.Box({ - children: [ - Widget.Box({ - vertical: false, - children: [ - Widget.Box({ - className: 'overview-search-results-icon', - homogeneous: true, - child: Widget.Icon({ - icon: app.iconName, - }), - }), - Widget.Label({ - className: 'overview-search-results-txt txt txt-norm', - label: app.name, - }), - Widget.Box({ hexpand: true }), - actionTextRevealer, - ] - }) - ] - }), - setup: (self) => self - .on('focus-in-event', (button) => { - actionText.revealChild = true; - actionTextRevealer.revealChild = true; - }) - .on('focus-out-event', (button) => { - actionText.revealChild = false; - actionTextRevealer.revealChild = false; - }) - , - }) -} - -export const ExecuteCommandButton = ({ command, terminal = false }) => searchItem({ - materialIconName: `${terminal ? 'terminal' : ' '}`, - name: `Run command`, - actionName: `Execute ${terminal ? 'in terminal' : ''}`, - content: `${command}`, - onActivate: () => execAndClose(command, terminal), - extraClassName: 'techfont', -}) - -export const CustomCommandButton = ({ text = '' }) => searchItem({ - materialIconName: ' ', - name: 'Action', - actionName: 'Run', - content: `${text}`, - onActivate: () => { - App.closeWindow('overview'); - launchCustomCommand(text); - }, -}); - -export const SearchButton = ({ text = '' }) => searchItem({ - materialIconName: '󰜏 ', - name: 'Search the web', - actionName: 'Go', - content: `${text}`, - onActivate: () => { - App.closeWindow('overview'); - execAsync(['bash', '-c', `xdg-open '${userOptions.search.engineBaseUrl}${text} ${['', ...userOptions.search.excludedSites].join(' -site:')}' &`]).catch(print); - }, -}); \ No newline at end of file diff --git a/config/ags/modules/overview/searchitem.js b/config/ags/modules/overview/searchitem.js deleted file mode 100644 index 2a3303a4..00000000 --- a/config/ags/modules/overview/searchitem.js +++ /dev/null @@ -1,65 +0,0 @@ -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; - -export const searchItem = ({ materialIconName, name, actionName, content, onActivate, extraClassName = '', ...rest }) => { - const actionText = Widget.Revealer({ - revealChild: false, - transition: "crossfade", - transitionDuration: userOptions.animations.durationLarge, - child: Widget.Label({ - className: 'overview-search-results-txt txt txt-small txt-action', - label: `${actionName}`, - }) - }); - const actionTextRevealer = Widget.Revealer({ - revealChild: false, - transition: "slide_left", - transitionDuration: userOptions.animations.durationSmall, - child: actionText, - }) - return Widget.Button({ - className: `overview-search-result-btn txt ${extraClassName}`, - onClicked: onActivate, - child: Widget.Box({ - children: [ - Widget.Box({ - vertical: false, - children: [ - Widget.Label({ - className: `icon-material overview-search-results-icon`, - label: `${materialIconName}`, - }), - Widget.Box({ - vertical: true, - children: [ - Widget.Label({ - hpack: 'start', - className: 'overview-search-results-txt txt-smallie txt-subtext', - label: `${name}`, - truncate: "end", - }), - Widget.Label({ - hpack: 'start', - className: 'overview-search-results-txt txt-norm', - label: `${content}`, - truncate: "end", - }), - ] - }), - Widget.Box({ hexpand: true }), - actionTextRevealer, - ], - }) - ] - }), - setup: (self) => self - .on('focus-in-event', (button) => { - actionText.revealChild = true; - actionTextRevealer.revealChild = true; - }) - .on('focus-out-event', (button) => { - actionText.revealChild = false; - actionTextRevealer.revealChild = false; - }) - , - }); -} diff --git a/config/ags/modules/overview/windowcontent.js b/config/ags/modules/overview/windowcontent.js deleted file mode 100644 index 7a19dd3c..00000000 --- a/config/ags/modules/overview/windowcontent.js +++ /dev/null @@ -1,262 +0,0 @@ -const { Gdk, Gtk } = imports.gi; -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; - -import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; -const { execAsync, exec } = Utils; -import { execAndClose, expandTilde, hasUnterminatedBackslash, couldBeMath, launchCustomCommand, ls } from './miscfunctions.js'; -import { - CalculationResultButton, CustomCommandButton, DirectoryButton, - DesktopEntryButton, ExecuteCommandButton, SearchButton -} from './searchbuttons.js'; -import { checkKeybind } from '../.widgetutils/keybind.js'; - -// Add math funcs -const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math; -const pi = Math.PI; -// trigonometric funcs for deg -const sind = x => sin(x * pi / 180); -const cosd = x => cos(x * pi / 180); -const tand = x => tan(x * pi / 180); -const cotd = x => cot(x * pi / 180); -const asind = x => asin(x) * 180 / pi; -const acosd = x => acos(x) * 180 / pi; -const atand = x => atan(x) * 180 / pi; -const acotd = x => acot(x) * 180 / pi; - -const MAX_RESULTS = 10; -const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size -const OVERVIEW_WS_NUM_SCALE = 0.0; -const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07; -const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; - -function iconExists(iconName) { - let iconTheme = Gtk.IconTheme.get_default(); - return iconTheme.has_icon(iconName); -} - -const OptionalOverview = async () => { - try { - return (await import('./overview_hyprland.js')).default(); - } catch { - return Widget.Box({}); - // return (await import('./overview_hyprland.js')).default(); - } -}; - -const overviewContent = await OptionalOverview(); - -export const SearchAndWindows = () => { - var _appSearchResults = []; - - const ClickToClose = ({ ...props }) => Widget.EventBox({ - ...props, - onPrimaryClick: () => App.closeWindow('overview'), - onSecondaryClick: () => App.closeWindow('overview'), - onMiddleClick: () => App.closeWindow('overview'), - }); - const resultsBox = Widget.Box({ - className: 'overview-search-results', - vertical: true, - vexpand: true, - }); - const resultsRevealer = Widget.Revealer({ - transitionDuration: userOptions.animations.durationLarge, - revealChild: false, - transition: 'slide_down', - // duration: 200, - hpack: 'center', - child: resultsBox, - }); - const entryPromptRevealer = Widget.Revealer({ - transition: 'crossfade', - transitionDuration: userOptions.animations.durationLarge, - revealChild: true, - hpack: 'center', - child: Widget.Label({ - className: 'overview-search-prompt txt-small txt', - label: 'Type to search' - }), - }); - - const entryIconRevealer = Widget.Revealer({ - transition: 'crossfade', - transitionDuration: userOptions.animations.durationLarge, - revealChild: false, - hpack: 'end', - child: Widget.Label({ - className: 'txt txt-large icon-material overview-search-icon', - label: ' ', - }), - }); - - const entryIcon = Widget.Box({ - className: 'overview-search-prompt-box', - setup: box => box.pack_start(entryIconRevealer, true, true, 0), - }); - - const entry = Widget.Entry({ - className: 'overview-search-box txt-small txt', - hpack: 'center', - onAccept: (self) => { // This is when you hit Enter - const text = self.text; - if (text.length == 0) return; - const isAction = text.startsWith('>'); - const isDir = (['/', '~'].includes(entry.text[0])); - - if (couldBeMath(text)) { // Eval on typing is dangerous, this is a workaround - try { - const fullResult = eval(text.replace(/\^/g, "**")); - // copy - execAsync(['wl-copy', `${fullResult}`]).catch(print); - App.closeWindow('overview'); - return; - } catch (e) { - // console.log(e); - } - } - if (isDir) { - App.closeWindow('overview'); - execAsync(['bash', '-c', `xdg-open "${expandTilde(text)}"`, `&`]).catch(print); - return; - } - if (_appSearchResults.length > 0) { - App.closeWindow('overview'); - _appSearchResults[0].launch(); - return; - } - else if (text[0] == '>') { // Custom commands - App.closeWindow('overview'); - launchCustomCommand(text); - return; - } - // Fallback: Execute command - if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { - if (text.startsWith('sudo')) - execAndClose(text, true); - else - execAndClose(text, false); - } - - else { - App.closeWindow('overview'); - execAsync(['bash', '-c', `xdg-open '${userOptions.search.engineBaseUrl}${text} ${['', ...userOptions.search.excludedSites].join(' -site:')}' &`]).catch(print); - } - }, - onChange: (entry) => { // this is when you type - const isAction = entry.text[0] == '>'; - const isDir = (['/', '~'].includes(entry.text[0])); - resultsBox.get_children().forEach(ch => ch.destroy()); - - // check empty if so then dont do stuff - if (entry.text == '') { - resultsRevealer.revealChild = false; - overviewContent.revealChild = true; - entryPromptRevealer.revealChild = true; - entryIconRevealer.revealChild = false; - entry.toggleClassName('overview-search-box-extended', false); - return; - } - const text = entry.text; - resultsRevealer.revealChild = true; - overviewContent.revealChild = false; - entryPromptRevealer.revealChild = false; - entryIconRevealer.revealChild = true; - entry.toggleClassName('overview-search-box-extended', true); - _appSearchResults = Applications.query(text); - - // Calculate - if (couldBeMath(text)) { // Eval on typing is dangerous; this is a small workaround. - try { - const fullResult = eval(text.replace(/\^/g, "**")); - resultsBox.add(CalculationResultButton({ result: fullResult, text: text })); - } catch (e) { - // console.log(e); - } - } - if (isDir) { - var contents = []; - contents = ls({ path: text, silent: true }); - contents.forEach((item) => { - resultsBox.add(DirectoryButton(item)); - }) - } - if (isAction) { // Eval on typing is dangerous, this is a workaround. - resultsBox.add(CustomCommandButton({ text: entry.text })); - } - // Add application entries - let appsToAdd = MAX_RESULTS; - _appSearchResults.forEach(app => { - if (appsToAdd == 0) return; - resultsBox.add(DesktopEntryButton(app)); - appsToAdd--; - }); - - // Fallbacks - // if the first word is an actual command - if (!isAction && !hasUnterminatedBackslash(text) && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { - resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') })); - } - - // Add fallback: search - resultsBox.add(SearchButton({ text: entry.text })); - resultsBox.show_all(); - }, - }); - return Widget.Box({ - vertical: true, - children: [ - ClickToClose({ // Top margin. Also works as a click-outside-to-close thing - child: Widget.Box({ - className: 'bar-height', - }) - }), - Widget.Box({ - hpack: 'center', - children: [ - entry, - Widget.Box({ - className: 'overview-search-icon-box', - setup: (box) => { - box.pack_start(entryPromptRevealer, true, true, 0) - }, - }), - entryIcon, - ] - }), - overviewContent, - resultsRevealer, - ], - setup: (self) => self - .hook(App, (_b, name, visible) => { - if (name == 'overview' && !visible) { - resultsBox.children = []; - entry.set_text(''); - } - }) - .on('key-press-event', (widget, event) => { // Typing - const keyval = event.get_keyval()[1]; - const modstate = event.get_state()[1]; - if (checkKeybind(event, userOptions.keybinds.overview.altMoveLeft)) - entry.set_position(Math.max(entry.get_position() - 1, 0)); - else if (checkKeybind(event, userOptions.keybinds.overview.altMoveRight)) - entry.set_position(Math.min(entry.get_position() + 1, entry.get_text().length)); - else if (checkKeybind(event, userOptions.keybinds.overview.deleteToEnd)) { - const text = entry.get_text(); - const pos = entry.get_position(); - const newText = text.slice(0, pos); - entry.set_text(newText); - entry.set_position(newText.length); - } - else if (!(modstate & Gdk.ModifierType.CONTROL_MASK)) { // Ctrl not held - if (keyval >= 32 && keyval <= 126 && widget != entry) { - Utils.timeout(1, () => entry.grab_focus()); - entry.set_text(entry.text + String.fromCharCode(keyval)); - entry.set_position(-1); - } - } - }) - , - }); -}; diff --git a/config/ags/user/style.css b/config/ags/user/style.css deleted file mode 100644 index 13410ea4..00000000 --- a/config/ags/user/style.css +++ /dev/null @@ -1,197 +0,0 @@ -*:not(popover) { - all: unset; -} - -@import '../../../.config/waybar/wallust/colors-waybar.css'; - -/* define some colors */ -@define-color border-color @color12; -@define-color border-color-alt @color9; -@define-color text-color rgba(255, 255, 255, 0.7); -@define-color noti-bg rgba(0, 0, 0, 0.4); -@define-color noti-bg-alt #111111; - -widget { - border-radius: 0.818rem; - -gtk-outline-radius: 0.818rem; -} - -.overview-window { - margin-top: 2.727rem; -} - -.overview-search-box { - transition: 300ms cubic-bezier(0, 0.55, 0.45, 1); - border-radius: 1.705rem; - -gtk-outline-radius: 1.705rem; - border-top: 4px solid @border-color; - border-left: 1px solid @border-color-alt; - border-right: 1px solid @border-color-alt; - border-bottom: 4px solid @border-color; - box-shadow: 0px 2px 3px alpha(@color12, 0.45); - margin: 0.476rem; - min-width: 13.636rem; - min-height: 3.409rem; - padding: 0rem 1.364rem; - padding-right: 2.864rem; - background-color: @noti-bg; - color: @text-color; - caret-color: inherit; - font-weight: bolder; -} -.overview-search-box selection { - background-color: @noti-bg; - color: @text-color; -} - -.overview-search-box-extended { - min-width: 25.909rem; - caret-color: #FDD9FD; -} - -.overview-search-prompt { - color: @text-color; -} - -.overview-search-icon { - margin: 0rem 1.023rem; -} - -.overview-search-prompt-box { - margin-left: -18.545rem; - margin-right: 0.544rem; -} - -.overview-search-icon-box { - margin-left: -18.545rem; - margin-right: 0.544rem; -} - -.overview-search-results { - border-radius: 1.705rem; - -gtk-outline-radius: 1.705rem; - border-top: 4px solid @border-color; - border-left: 1px solid @border-color-alt; - border-right: 1px solid @border-color-alt; - border-bottom: 4px solid @border-color; - box-shadow: 0px 2px 3px @color9; - margin: 0.476rem; - min-width: 28.773rem; - padding: 0.682rem; - background-color: @noti-bg; - color: @text-color; - font-weight: bold; -} - -.overview-search-results-icon { - margin: 0rem 0.682rem; - font-size: 2.386rem; - min-width: 2.386rem; - min-height: 2.386rem; -} - -.overview-search-results-txt { - margin-right: 0.682rem; -} - -.overview-search-results-txt-cmd { - margin-right: 0.682rem; - font-family: "JetBrains Mono NF", "JetBrains Mono Nerd Font", "JetBrains Mono NL", "SpaceMono NF", "SpaceMono Nerd Font", monospace; - font-size: 1.227rem; -} - -.overview-search-result-btn { - border-radius: 1.159rem; - -gtk-outline-radius: 1.159rem; - padding: 0.341rem; - min-width: 2.386rem; - min-height: 2.386rem; - caret-color: transparent; -} - -.overview-search-result-btn:hover, -.overview-search-result-btn:focus { - background-color: alpha(@color7, 0.9); - color: alpha(@color0, 0.7); -} - -.overview-search-result-btn:active { - background-color: alpha(@color7, 0.9); - color: @color4; -} - -.overview-tasks { - border-radius: 1.705rem; - -gtk-outline-radius: 1.705rem; - border-top: 4px solid @border-color; - border-left: 1px solid @border-color-alt; - border-right: 1px solid @border-color-alt; - border-bottom: 4px solid @border-color; - box-shadow: 0px 2px 3px @color5; - margin: 0.476rem; - padding: 0.341rem; - /* background-color: rgba(49, 50, 68, 0.8); */ - background-color: @noti-bg; - color: #EBDFED; -} - -.overview-tasks-workspace { - border-radius: 1.159rem; - -gtk-outline-radius: 1.159rem; - margin: 0.341rem; - /* background-color: #26233A; */ - background-image: url('../../rofi/.current_wallpaper'); - background-size: cover; - background-position: center; - border: 0.068rem solid alpha(@color4, 0.5); -} - -.overview-tasks-workspace-number { - font-family: "Open Sans", "Noto Sans", sans-serif; - color: #CFC2D3; -} - -.overview-tasks-window { - border-radius: 1.159rem; - -gtk-outline-radius: 1.159rem; - transition: 300ms cubic-bezier(0.1, 1, 0, 1); - background-color: alpha(@color3, .7); - /* background-color: @color_a3; */ - /* background-color: rgba(46, 40, 50, 0.8); */ - color: #EBDFED; - border: 0.068rem solid @color7; -} - -.overview-tasks-window:hover, -.overview-tasks-window:focus { - background-color: alpha(@color9, 0.8); -} - -.overview-tasks-window:active { - background-color: alpha(@color9, 0.8); -} - -.overview-tasks-window-selected { - background-color: alpha(@color9, 0.8); -} - -.overview-tasks-window-dragging { - opacity: 0.2; -} - -.growingRadial { - transition: 300ms cubic-bezier(0.2, 0, 0, 1); -} - -.fadingRadial { - transition: 50ms cubic-bezier(0.2, 0, 0, 1); -} - -.sidebar-pinned { - margin: 0rem; - border-radius: 0rem; - border-bottom-right-radius: 1.705rem; - border: 0rem solid; -} - -/*# sourceMappingURL=style.css.map */ diff --git a/config/ags/user_options.js b/config/ags/user_options.js deleted file mode 100644 index 9105decb..00000000 --- a/config/ags/user_options.js +++ /dev/null @@ -1,21 +0,0 @@ - -const userConfigOptions = { - // For every option, see ~/.config/ags/modules/.configuration/user_options.js - // (vscode users ctrl+click this: file://./modules/.configuration/user_options.js) - // (vim users: `:vsp` to split window, move cursor to this path, press `gf`. `Ctrl-w` twice to switch between) - // options listed in this file will override the default ones in the above file - // Here's an example - 'overview':{ - 'scale': 0.15, - 'numOfRows': 2 - }, - 'keybinds': { - 'sidebar': { - 'pin': "Ctrl+p", - 'nextTab': "Ctrl+Page_Down", - 'prevTab': "Ctrl+Page_Up", - }, - }, -} - -export default userConfigOptions; diff --git a/config/ags/variables.js b/config/ags/variables.js deleted file mode 100644 index 645a5807..00000000 --- a/config/ags/variables.js +++ /dev/null @@ -1,21 +0,0 @@ -const { Gtk } = imports.gi; -import Variable from 'resource:///com/github/Aylur/ags/variable.js'; -import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; -const { exec, execAsync } = Utils; - -Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets/icons`); - -// Screen size -export const SCREEN_WIDTH = Number(exec(`bash -c "hyprctl monitors -j | jq '.[0].width / .[0].scale'"`)); -export const SCREEN_HEIGHT = Number(exec(`bash -c "hyprctl monitors -j | jq '.[0].height / .[0].scale'"`)); - -// Mode switching -export const currentShellMode = Variable('normal', {}) // normal, focus -globalThis['currentMode'] = currentShellMode; -globalThis['cycleMode'] = () => { - if (currentShellMode.value === 'normal') { - currentShellMode.value = 'focus'; - } else { - currentShellMode.value = 'normal'; - } -} -- cgit v1.2.3 From 7a3bd4f9fe48aad02ef8b13d3677b3bd8358252c Mon Sep 17 00:00:00 2001 From: "Ja.KooLit" <85185940+JaKooLit@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:45:52 +0900 Subject: Update Brightness.sh fixes upper case as it does not work --- config/hypr/scripts/Brightness.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/Brightness.sh b/config/hypr/scripts/Brightness.sh index 3e283c11..91ad5f05 100755 --- a/config/hypr/scripts/Brightness.sh +++ b/config/hypr/scripts/Brightness.sh @@ -18,7 +18,7 @@ get_icon_path() { if (( level > 100 )); then level=100 fi - echo "$ICON_DIR/brightness-${level}.png" + echo "$iDIR/brightness-${level}.png" } # Send notification @@ -58,10 +58,10 @@ case "$1" in get_brightness ;; "--inc") - change_brightness "$STEP" + change_brightness "$step" ;; "--dec") - change_brightness "-$STEP" + change_brightness "-$step" ;; *) get_brightness -- cgit v1.2.3 From f43fe874dbc7561416997dd8e74df0f25621bdcd Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Wed, 16 Jul 2025 22:23:23 +0900 Subject: remove ags from startup --- config/hypr/UserConfigs/Startup_Apps.conf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'config') diff --git a/config/hypr/UserConfigs/Startup_Apps.conf b/config/hypr/UserConfigs/Startup_Apps.conf index 91ff9cd0..63f2ee68 100644 --- a/config/hypr/UserConfigs/Startup_Apps.conf +++ b/config/hypr/UserConfigs/Startup_Apps.conf @@ -26,11 +26,10 @@ exec-once = $scriptsDir/Polkit.sh # starup apps exec-once = nm-applet --indicator exec-once = swaync -#exec-once = ags #exec-once = blueman-applet #exec-once = rog-control-center exec-once = waybar -exec-once = qs +exec-once = qs # quickshell AGS Desktop Overview alternative #clipboard manager exec-once = wl-paste --type text --watch cliphist store -- cgit v1.2.3 From d36df085bf093ce6f8092eeb6e6878d9ad2791d0 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Thu, 17 Jul 2025 00:58:43 +0900 Subject: integrated wallust colors into simple_sddm_2 SDDM theme --- config/hypr/UserScripts/WallpaperEffects.sh | 8 ++--- config/hypr/UserScripts/WallpaperSelect.sh | 10 +++--- config/hypr/scripts/sddm_wallpaper.sh | 56 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 11 deletions(-) create mode 100755 config/hypr/scripts/sddm_wallpaper.sh (limited to 'config') diff --git a/config/hypr/UserScripts/WallpaperEffects.sh b/config/hypr/UserScripts/WallpaperEffects.sh index 3717543c..2ba58d0c 100755 --- a/config/hypr/UserScripts/WallpaperEffects.sh +++ b/config/hypr/UserScripts/WallpaperEffects.sh @@ -131,10 +131,8 @@ if [[ -n "$choice" ]]; then exit 1 fi - # Open terminal and set the wallpaper - $terminal -e bash -c "echo 'Enter your password to set wallpaper as SDDM Background'; \ - sudo cp -r $wallpaper_output '$sddm_simple/Backgrounds/default' && \ - notify-send -i '$iDIR/ja.png' 'SDDM' 'Background SET'" + exec $SCRIPTSDIR/sddm_wallpaper.sh --effects + fi fi -fi +fi \ No newline at end of file diff --git a/config/hypr/UserScripts/WallpaperSelect.sh b/config/hypr/UserScripts/WallpaperSelect.sh index 47a8f2cd..a6e6c4d4 100755 --- a/config/hypr/UserScripts/WallpaperSelect.sh +++ b/config/hypr/UserScripts/WallpaperSelect.sh @@ -123,11 +123,9 @@ set_sddm_wallpaper() { notify-send -i "$iDIR/error.png" "Missing $terminal" "Install $terminal to enable setting of wallpaper background" exit 1 fi - - # Open terminal to enter password - $terminal -e bash -c "echo 'Enter your password to set wallpaper as SDDM Background'; \ - sudo cp -r $wallpaper_current '$sddm_simple/Backgrounds/default' && \ - notify-send -i '$iDIR/ja.png' 'SDDM' 'Background SET'" + + exec $SCRIPTSDIR/sddm_wallpaper.sh --normal + fi fi } @@ -235,4 +233,4 @@ if pidof rofi >/dev/null; then pkill rofi fi -main +main \ No newline at end of file diff --git a/config/hypr/scripts/sddm_wallpaper.sh b/config/hypr/scripts/sddm_wallpaper.sh new file mode 100755 index 00000000..fd385fcd --- /dev/null +++ b/config/hypr/scripts/sddm_wallpaper.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ +# SDDM Wallpaper and Wallust Colors Setter + +# variables +terminal=kitty +wallDIR="$HOME/Pictures/wallpapers" +SCRIPTSDIR="$HOME/.config/hypr/scripts" +wallpaper_current="$HOME/.config/hypr/wallpaper_effects/.wallpaper_current" +wallpaper_modified="$HOME/.config/hypr/wallpaper_effects/.wallpaper_modified" +sddm_simple="/usr/share/sddm/themes/simple_sddm_2" + +# rofi-wallust-sddm colors path +rofi_wallust="$HOME/.config/rofi/wallust/colors-rofi.rasi" +sddm_theme_conf="$sddm_simple/theme.conf" + +# Directory for swaync +iDIR="$HOME/.config/swaync/images" +iDIRi="$HOME/.config/swaync/icons" + +# Parse arguments +mode="effects" # default +if [[ "$1" == "--normal" ]]; then + mode="normal" +elif [[ "$1" == "--effects" ]]; then + mode="effects" +fi + +# Extract colors from rofi wallust config +main_color=$(grep -oP 'color13:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +accent_color=$(grep -oP 'color12:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +bg_color=$(grep -oP 'color0:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +placeholder_color="$accent_color" +icon_color=$(grep -oP 'foreground:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") + +# wallpaper to use +if [[ "$mode" == "normal" ]]; then + wallpaper_path="$wallpaper_current" +else + wallpaper_path="$wallpaper_modified" +fi + +# Launch terminal and apply changes +$terminal -e bash -c " +echo 'Enter your password to update SDDM wallpapers and colors'; +sudo sed -i \"s/MainColor=\\\"#.*\\\"/MainColor=\\\"$main_color\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/AccentColor=\\\"#.*\\\"/AccentColor=\\\"$accent_color\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/BackgroundColor=\\\"#.*\\\"/BackgroundColor=\\\"$bg_color\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/placeholderColor=\\\"#.*\\\"/placeholderColor=\\\"$placeholder_color\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/IconColor=\\\"#.*\\\"/IconColor=\\\"$icon_color\\\"/\" \"$sddm_theme_conf\" + +# Copy wallpaper +sudo cp \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default\" + +notify-send -i \"$iDIR/ja.png\" \"SDDM\" \"Background SET\" +" -- cgit v1.2.3 From 3c1cc04020678b76a622a1a7a28a2c1e529f7665 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Sat, 19 Jul 2025 17:27:14 +0900 Subject: updated copy.sh and still added ags --- config/ags/config.js | 31 ++ config/ags/modules/.configuration/user_options.js | 127 +++++++ config/ags/modules/.miscutils/icons.js | 13 + config/ags/modules/.miscutils/mathfuncs.js | 4 + config/ags/modules/.miscutils/system.js | 54 +++ .../ags/modules/.widgethacks/advancedrevealers.js | 86 +++++ config/ags/modules/.widgethacks/popupwindow.js | 32 ++ config/ags/modules/.widgetutils/clickthrough.js | 4 + config/ags/modules/.widgetutils/cursorhover.js | 57 +++ config/ags/modules/.widgetutils/keybind.js | 25 ++ config/ags/modules/overview/actions.js | 28 ++ config/ags/modules/overview/main.js | 18 + config/ags/modules/overview/miscfunctions.js | 155 ++++++++ config/ags/modules/overview/overview_hyprland.js | 423 +++++++++++++++++++++ config/ags/modules/overview/searchbuttons.js | 163 ++++++++ config/ags/modules/overview/searchitem.js | 65 ++++ config/ags/modules/overview/windowcontent.js | 262 +++++++++++++ config/ags/user/style.css | 197 ++++++++++ config/ags/user_options.js | 21 + config/ags/variables.js | 21 + config/hypr/UserConfigs/Startup_Apps.conf | 1 + config/hypr/UserConfigs/UserKeybinds.conf | 5 +- copy.sh | 60 +++ 23 files changed, 1850 insertions(+), 2 deletions(-) create mode 100644 config/ags/config.js create mode 100644 config/ags/modules/.configuration/user_options.js create mode 100644 config/ags/modules/.miscutils/icons.js create mode 100644 config/ags/modules/.miscutils/mathfuncs.js create mode 100644 config/ags/modules/.miscutils/system.js create mode 100644 config/ags/modules/.widgethacks/advancedrevealers.js create mode 100644 config/ags/modules/.widgethacks/popupwindow.js create mode 100644 config/ags/modules/.widgetutils/clickthrough.js create mode 100644 config/ags/modules/.widgetutils/cursorhover.js create mode 100644 config/ags/modules/.widgetutils/keybind.js create mode 100644 config/ags/modules/overview/actions.js create mode 100644 config/ags/modules/overview/main.js create mode 100644 config/ags/modules/overview/miscfunctions.js create mode 100644 config/ags/modules/overview/overview_hyprland.js create mode 100644 config/ags/modules/overview/searchbuttons.js create mode 100644 config/ags/modules/overview/searchitem.js create mode 100644 config/ags/modules/overview/windowcontent.js create mode 100644 config/ags/user/style.css create mode 100644 config/ags/user_options.js create mode 100644 config/ags/variables.js (limited to 'config') diff --git a/config/ags/config.js b/config/ags/config.js new file mode 100644 index 00000000..278bf351 --- /dev/null +++ b/config/ags/config.js @@ -0,0 +1,31 @@ +"use strict"; +import GLib from 'gi://GLib'; +import App from 'resource:///com/github/Aylur/ags/app.js' +import userOptions from './modules/.configuration/user_options.js'; +import Overview from './modules/overview/main.js'; + +const COMPILED_STYLE_DIR = `${GLib.get_user_config_dir()}/ags/user/` + +async function applyStyle() { + + App.resetCss(); + App.applyCss(`${COMPILED_STYLE_DIR}/style.css`); + console.log('[LOG] Styles loaded') +} +applyStyle().catch(print); + +const Windows = () => [ + Overview() +]; +const CLOSE_ANIM_TIME = 210; +App.config({ + css: `${COMPILED_STYLE_DIR}/style.css`, + stackTraceOnError: true, + closeWindowDelay: { + 'sideright': CLOSE_ANIM_TIME, + 'sideleft': CLOSE_ANIM_TIME, + 'osk': CLOSE_ANIM_TIME, + }, + windows: Windows().flat(1), +}); + diff --git a/config/ags/modules/.configuration/user_options.js b/config/ags/modules/.configuration/user_options.js new file mode 100644 index 00000000..242c0575 --- /dev/null +++ b/config/ags/modules/.configuration/user_options.js @@ -0,0 +1,127 @@ + +import userOverrides from '../../user_options.js'; + +// Defaults +let configOptions = { + // General stuff + 'ai': { + 'defaultGPTProvider': "openai", + 'defaultTemperature': 0.9, + 'enhancements': true, + 'useHistory': true, + 'writingCursor': " ...", // Warning: Using weird characters can mess up Markdown rendering + }, + 'animations': { + 'choreographyDelay': 35, + 'durationSmall': 110, + 'durationLarge': 180, + }, + 'appearance': { + 'keyboardUseFlag': false, // Use flag emoji instead of abbreviation letters + }, + 'apps': { + 'imageViewer': "loupe", + 'terminal': "foot", // This is only for shell actions + }, + 'battery': { + 'low': 20, + 'critical': 10, + }, + 'music': { + 'preferredPlayer': "plasma-browser-integration", + }, + 'onScreenKeyboard': { + 'layout': "qwerty_full", // See modules/onscreenkeyboard/onscreenkeyboard.js for available layouts + }, + 'overview': { + 'scale': 0.18, // Relative to screen size + 'numOfRows': 2, + 'numOfCols': 5, + 'wsNumScale': 0.09, + 'wsNumMarginScale': 0.07, + }, + 'sidebar': { + 'imageColumns': 2, + 'imageBooruCount': 20, + 'imageAllowNsfw': false, + }, + 'search': { + 'engineBaseUrl': "https://www.google.com/search?q=", + 'excludedSites': [], //add site to exclude from result. eg: "quora.com" + }, + 'time': { + // See https://docs.gtk.org/glib/method.DateTime.format.html + // Here's the 12h format: "%I:%M%P" + // For seconds, add "%S" and set interval to 1000 + 'format': "%H:%M", + 'interval': 5000, + 'dateFormatLong': "%A, %d/%m", // On bar + 'dateInterval': 5000, + 'dateFormat': "%d/%m", // On notif time + }, + 'weather': { + 'city': "", + }, + 'workspaces': { + 'shown': 10, + }, + // Longer stuff + 'icons': { + substitutions: { + 'codium-url-handler': "vscodium", + 'codium': "vscodium", + 'code-url-handler': "visual-studio-code", + 'Code': "visual-studio-code", + 'GitHub Desktop': "github-desktop", + 'Minecraft* 1.20.1': "minecraft", + 'gnome-tweaks': "org.gnome.tweaks", + 'pavucontrol-qt': "pavucontrol", + 'eu.betterbird.Betterbird' : "thunderbird", + 'thunderbird-esr': "thunderbird", + 'wps': "wps-office2019-kprometheus", + 'wpsoffice': "wps-office2019-kprometheus", + 'firefox-esr': "firefox", + 'soffice' : "libreoffice", + '': "image-missing", + } + }, + 'keybinds': { + // Format: Mod1+Mod2+key. CaSe SeNsItIvE! + // Modifiers: Shift Ctrl Alt Hyper Meta + // See https://docs.gtk.org/gdk3/index.html#constants for the other keys (they are listed as KEY_key) + 'overview': { + 'altMoveLeft': "Ctrl+b", + 'altMoveRight': "Ctrl+f", + 'deleteToEnd': "Ctrl+k", + }, + 'sidebar': { + 'apis': { + 'nextTab': "Page_Down", + 'prevTab': "Page_Up", + }, + 'options': { // Right sidebar + 'nextTab': "Page_Down", + 'prevTab': "Page_Up", + }, + 'pin': "Ctrl+p", + 'cycleTab': "Ctrl+Tab", + 'nextTab': "Ctrl+Page_Down", + 'prevTab': "Ctrl+Page_Up", + }, + }, +} + +// Override defaults with user's options +function overrideConfigRecursive(userOverrides, configOptions = {}) { + for (const [key, value] of Object.entries(userOverrides)) { + if (typeof value === 'object') { + overrideConfigRecursive(value, configOptions[key]); + } else { + configOptions[key] = value; + } + } +} +overrideConfigRecursive(userOverrides, configOptions); + +globalThis['userOptions'] = configOptions; +export default configOptions; \ No newline at end of file diff --git a/config/ags/modules/.miscutils/icons.js b/config/ags/modules/.miscutils/icons.js new file mode 100644 index 00000000..fb1e20da --- /dev/null +++ b/config/ags/modules/.miscutils/icons.js @@ -0,0 +1,13 @@ +const { Gtk } = imports.gi; + +export function iconExists(iconName) { + let iconTheme = Gtk.IconTheme.get_default(); + return iconTheme.has_icon(iconName); +} + +export function substitute(str) { + if(userOptions.icons.substitutions[str]) return userOptions.icons.substitutions[str]; + + if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case + return str; +} \ No newline at end of file diff --git a/config/ags/modules/.miscutils/mathfuncs.js b/config/ags/modules/.miscutils/mathfuncs.js new file mode 100644 index 00000000..ba1c0b59 --- /dev/null +++ b/config/ags/modules/.miscutils/mathfuncs.js @@ -0,0 +1,4 @@ + +export function clamp(x, min, max) { + return Math.min(Math.max(x, min), max); +} \ No newline at end of file diff --git a/config/ags/modules/.miscutils/system.js b/config/ags/modules/.miscutils/system.js new file mode 100644 index 00000000..ef7d15c3 --- /dev/null +++ b/config/ags/modules/.miscutils/system.js @@ -0,0 +1,54 @@ +const { GLib } = imports.gi; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { execAsync, exec } = Utils; + +export const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2 | sed "s/\\"//g"'`).trim(); +export const isDebianDistro = (distroID == 'linuxmint' || distroID == 'ubuntu' || distroID == 'debian' || distroID == 'zorin' || distroID == 'popos' || distroID == 'raspbian' || distroID == 'kali'); +export const isArchDistro = (distroID == 'arch' || distroID == 'endeavouros' || distroID == 'cachyos'); +export const hasFlatpak = !!exec(`bash -c 'command -v flatpak'`); + +const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/colormode.txt`; +const colorMode = Utils.exec('bash -c "sed -n \'1p\' $HOME/.cache/ags/user/colormode.txt"'); +export let darkMode = Variable(!(Utils.readFile(LIGHTDARK_FILE_LOCATION).split('\n')[0].trim() == 'light')); +export const hasPlasmaIntegration = !!Utils.exec('bash -c "command -v plasma-browser-integration-host"'); + +export const getDistroIcon = () => { + // Arches + if(distroID == 'arch') return 'arch-symbolic'; + if(distroID == 'endeavouros') return 'endeavouros-symbolic'; + if(distroID == 'cachyos') return 'cachyos-symbolic'; + // Funny flake + if(distroID == 'nixos') return 'nixos-symbolic'; + // Cool thing + if(distroID == 'fedora') return 'fedora-symbolic'; + // Debians + if(distroID == 'linuxmint') return 'ubuntu-symbolic'; + if(distroID == 'ubuntu') return 'ubuntu-symbolic'; + if(distroID == 'debian') return 'debian-symbolic'; + if(distroID == 'zorin') return 'ubuntu-symbolic'; + if(distroID == 'popos') return 'ubuntu-symbolic'; + if(distroID == 'raspbian') return 'debian-symbolic'; + if(distroID == 'kali') return 'debian-symbolic'; + return 'linux-symbolic'; +} + +export const getDistroName = () => { + // Arches + if(distroID == 'arch') return 'Arch Linux'; + if(distroID == 'endeavouros') return 'EndeavourOS'; + if(distroID == 'cachyos') return 'CachyOS'; + // Funny flake + if(distroID == 'nixos') return 'NixOS'; + // Cool thing + if(distroID == 'fedora') return 'Fedora'; + // Debians + if(distroID == 'linuxmint') return 'Linux Mint'; + if(distroID == 'ubuntu') return 'Ubuntu'; + if(distroID == 'debian') return 'Debian'; + if(distroID == 'zorin') return 'Zorin'; + if(distroID == 'popos') return 'Pop!_OS'; + if(distroID == 'raspbian') return 'Raspbian'; + if(distroID == 'kali') return 'Kali Linux'; + return 'Linux'; +} diff --git a/config/ags/modules/.widgethacks/advancedrevealers.js b/config/ags/modules/.widgethacks/advancedrevealers.js new file mode 100644 index 00000000..3f127931 --- /dev/null +++ b/config/ags/modules/.widgethacks/advancedrevealers.js @@ -0,0 +1,86 @@ +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; + +const { Revealer, Scrollable } = Widget; + +export const MarginRevealer = ({ + transition = 'slide_down', + child, + revealChild, + showClass = 'element-show', // These are for animation curve, they don't really hide + hideClass = 'element-hide', // Don't put margins in these classes! + extraSetup = () => { }, + ...rest +}) => { + const widget = Scrollable({ + ...rest, + attribute: { + 'revealChild': true, // It'll be set to false after init if it's supposed to hide + 'transition': transition, + 'show': () => { + if (widget.attribute.revealChild) return; + widget.hscroll = 'never'; + widget.vscroll = 'never'; + child.toggleClassName(hideClass, false); + child.toggleClassName(showClass, true); + widget.attribute.revealChild = true; + child.css = 'margin: 0px;'; + }, + 'hide': () => { + if (!widget.attribute.revealChild) return; + child.toggleClassName(hideClass, true); + child.toggleClassName(showClass, false); + widget.attribute.revealChild = false; + if (widget.attribute.transition == 'slide_left') + child.css = `margin-right: -${child.get_allocated_width()}px;`; + else if (widget.attribute.transition == 'slide_right') + child.css = `margin-left: -${child.get_allocated_width()}px;`; + else if (widget.attribute.transition == 'slide_up') + child.css = `margin-bottom: -${child.get_allocated_height()}px;`; + else if (widget.attribute.transition == 'slide_down') + child.css = `margin-top: -${child.get_allocated_height()}px;`; + }, + 'toggle': () => { + if (widget.attribute.revealChild) widget.attribute.hide(); + else widget.attribute.show(); + }, + }, + child: child, + hscroll: `${revealChild ? 'never' : 'always'}`, + vscroll: `${revealChild ? 'never' : 'always'}`, + setup: (self) => { + extraSetup(self); + } + }); + child.toggleClassName(`${revealChild ? showClass : hideClass}`, true); + return widget; +} + +// TODO: Allow reveal update. Currently this just helps at declaration +export const DoubleRevealer = ({ + transition1 = 'slide_right', + transition2 = 'slide_left', + duration1 = 150, + duration2 = 150, + child, + revealChild, + ...rest +}) => { + const r2 = Revealer({ + transition: transition2, + transitionDuration: duration2, + revealChild: revealChild, + child: child, + }); + const r1 = Revealer({ + transition: transition1, + transitionDuration: duration1, + revealChild: revealChild, + child: r2, + ...rest, + }) + r1.toggleRevealChild = (value) => { + r1.revealChild = value; + r2.revealChild = value; + } + return r1; +} diff --git a/config/ags/modules/.widgethacks/popupwindow.js b/config/ags/modules/.widgethacks/popupwindow.js new file mode 100644 index 00000000..26dad59c --- /dev/null +++ b/config/ags/modules/.widgethacks/popupwindow.js @@ -0,0 +1,32 @@ +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +const { Box, Window } = Widget; + + +export default ({ + name, + child, + showClassName = "", + hideClassName = "", + ...props +}) => { + return Window({ + name, + visible: false, + layer: 'overlay', + ...props, + + child: Box({ + setup: (self) => { + self.hook(App, (self, currentName, visible) => { + if (currentName === name) { + self.toggleClassName(hideClassName, !visible); + } + }).keybind("Escape", () => App.closeWindow(name)) + if (showClassName !== "" && hideClassName !== "") + self.className = `${showClassName} ${hideClassName}`; + }, + child: child, + }), + }); +} \ No newline at end of file diff --git a/config/ags/modules/.widgetutils/clickthrough.js b/config/ags/modules/.widgetutils/clickthrough.js new file mode 100644 index 00000000..505f1412 --- /dev/null +++ b/config/ags/modules/.widgetutils/clickthrough.js @@ -0,0 +1,4 @@ +import Cairo from 'gi://cairo?version=1.0'; + +export const dummyRegion = new Cairo.Region(); +export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); \ No newline at end of file diff --git a/config/ags/modules/.widgetutils/cursorhover.js b/config/ags/modules/.widgetutils/cursorhover.js new file mode 100644 index 00000000..89be913b --- /dev/null +++ b/config/ags/modules/.widgetutils/cursorhover.js @@ -0,0 +1,57 @@ +const { Gdk } = imports.gi; + +export function setupCursorHover(button) { // Hand pointing cursor on hover + const display = Gdk.Display.get_default(); + button.connect('enter-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'pointer'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); + +} + +export function setupCursorHoverAim(button) { // Crosshair cursor on hover + button.connect('enter-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'crosshair'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} + +export function setupCursorHoverGrab(button) { // Hand ready to grab on hover + button.connect('enter-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'grab'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} + +export function setupCursorHoverInfo(button) { // "?" mark cursor on hover + const display = Gdk.Display.get_default(); + button.connect('enter-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'help'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} + diff --git a/config/ags/modules/.widgetutils/keybind.js b/config/ags/modules/.widgetutils/keybind.js new file mode 100644 index 00000000..eda7877b --- /dev/null +++ b/config/ags/modules/.widgetutils/keybind.js @@ -0,0 +1,25 @@ +const { Gdk } = imports.gi; + +const MODS = { + 'Shift': Gdk.ModifierType.SHIFT_MASK, + 'Ctrl': Gdk.ModifierType.CONTROL_MASK, + 'Alt': Gdk.ModifierType.ALT_MASK, + 'Hyper': Gdk.ModifierType.HYPER_MASK, + 'Meta': Gdk.ModifierType.META_MASK +} + +export const checkKeybind = (event, keybind) => { + const pressedModMask = event.get_state()[1]; + const pressedKey = event.get_keyval()[1]; + const keys = keybind.split('+'); + for (let i = 0; i < keys.length; i++) { + if (keys[i] in MODS) { + if (!(pressedModMask & MODS[keys[i]])) { + return false; + } + } else if (pressedKey !== Gdk[`KEY_${keys[i]}`]) { + return false; + } + } + return true; +} diff --git a/config/ags/modules/overview/actions.js b/config/ags/modules/overview/actions.js new file mode 100644 index 00000000..766cf454 --- /dev/null +++ b/config/ags/modules/overview/actions.js @@ -0,0 +1,28 @@ +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + +function moveClientToWorkspace(address, workspace) { + Utils.execAsync(['bash', '-c', `hyprctl dispatch movetoworkspacesilent ${workspace},address:${address} &`]); +} + +export function dumpToWorkspace(from, to) { + if (from == to) return; + Hyprland.clients.forEach(client => { + if (client.workspace.id == from) { + moveClientToWorkspace(client.address, to); + } + }); +} + +export function swapWorkspace(workspaceA, workspaceB) { + if (workspaceA == workspaceB) return; + const clientsA = []; + const clientsB = []; + Hyprland.clients.forEach(client => { + if (client.workspace.id == workspaceA) clientsA.push(client.address); + if (client.workspace.id == workspaceB) clientsB.push(client.address); + }); + + clientsA.forEach((address) => moveClientToWorkspace(address, workspaceB)); + clientsB.forEach((address) => moveClientToWorkspace(address, workspaceA)); +} \ No newline at end of file diff --git a/config/ags/modules/overview/main.js b/config/ags/modules/overview/main.js new file mode 100644 index 00000000..1f5348d9 --- /dev/null +++ b/config/ags/modules/overview/main.js @@ -0,0 +1,18 @@ +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import { SearchAndWindows } from "./windowcontent.js"; +import PopupWindow from '../.widgethacks/popupwindow.js'; + +export default (id = '') => PopupWindow({ + name: `overview${id}`, + exclusivity: 'ignore', + keymode: 'exclusive', + visible: false, + // anchor: ['middle'], + layer: 'overlay', + child: Widget.Box({ + vertical: true, + children: [ + SearchAndWindows(), + ] + }), +}) diff --git a/config/ags/modules/overview/miscfunctions.js b/config/ags/modules/overview/miscfunctions.js new file mode 100644 index 00000000..187ee6ec --- /dev/null +++ b/config/ags/modules/overview/miscfunctions.js @@ -0,0 +1,155 @@ +const { Gio, GLib } = imports.gi; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { execAsync, exec } = Utils; +// import Todo from "../../services/todo.js"; +import { darkMode } from '../.miscutils/system.js'; + +export function hasUnterminatedBackslash(inputString) { + // Use a regular expression to match a trailing odd number of backslashes + const regex = /\\+$/; + return regex.test(inputString); +} + +export function launchCustomCommand(command) { + const args = command.toLowerCase().split(' '); + if (args[0] == '>raw') { // Mouse raw input + Utils.execAsync('hyprctl -j getoption input:accel_profile') + .then((output) => { + const value = JSON.parse(output)["str"].trim(); + if (value != "[[EMPTY]]" && value != "") { + execAsync(['bash', '-c', `hyprctl keyword input:accel_profile '[[EMPTY]]'`]).catch(print); + } + else { + execAsync(['bash', '-c', `hyprctl keyword input:accel_profile flat`]).catch(print); + } + }) + } + else if (args[0] == '>img') { // Change wallpaper + execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchwall.sh`, `&`]).catch(print); + } + else if (args[0] == '>color') { // Generate colorscheme from color picker + execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh --pick`, `&`]).catch(print); + } + else if (args[0] == '>light') { // Light mode + darkMode.value = false; + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/light/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); + } + else if (args[0] == '>dark') { // Dark mode + darkMode.value = true; + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/dark/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); + } + else if (args[0] == '>badapple') { // Black and white + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "3s/.*/monochrome/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`]) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`])) + .catch(print); + } + else if (args[0] == '>material') { // Use material colors + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "material" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print)) + .catch(print); + } + else if (args[0] == '>pywal') { // Use Pywal (ik it looks shit but I'm not removing) + execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "pywal" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print) + .then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print)) + .catch(print); + } + else if (args[0] == '>todo') { // Todo + Todo.add(args.slice(1).join(' ')); + } + else if (args[0] == '>shutdown') { // Shut down + execAsync([`bash`, `-c`, `systemctl poweroff || loginctl poweroff`]).catch(print); + } + else if (args[0] == '>reboot') { // Reboot + execAsync([`bash`, `-c`, `systemctl reboot || loginctl reboot`]).catch(print); + } + else if (args[0] == '>sleep') { // Sleep + execAsync([`bash`, `-c`, `systemctl suspend || loginctl suspend`]).catch(print); + } + else if (args[0] == '>logout') { // Log out + execAsync([`bash`, `-c`, `pkill Hyprland || pkill sway`]).catch(print); + } +} + +export function execAndClose(command, terminal) { + App.closeWindow('overview'); + if (terminal) { + execAsync([`bash`, `-c`, `${userOptions.apps.terminal} fish -C "${command}"`, `&`]).catch(print); + } + else + execAsync(command).catch(print); +} + +export function couldBeMath(str) { + const regex = /^[0-9.+*/-]/; + return regex.test(str); +} + +export function expandTilde(path) { + if (path.startsWith('~')) { + return GLib.get_home_dir() + path.slice(1); + } else { + return path; + } +} + +function getFileIcon(fileInfo) { + let icon = fileInfo.get_icon(); + if (icon) { + // Get the icon's name + return icon.get_names()[0]; + } else { + // Default icon for files + return 'text-x-generic'; + } +} + +export function ls({ path = '~', silent = false }) { + let contents = []; + try { + let expandedPath = expandTilde(path); + if (expandedPath.endsWith('/')) + expandedPath = expandedPath.slice(0, -1); + let folder = Gio.File.new_for_path(expandedPath); + + let enumerator = folder.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); + let fileInfo; + while ((fileInfo = enumerator.next_file(null)) !== null) { + let fileName = fileInfo.get_display_name(); + let fileType = fileInfo.get_file_type(); + + let item = { + parentPath: expandedPath, + name: fileName, + type: fileType === Gio.FileType.DIRECTORY ? 'folder' : 'file', + icon: getFileIcon(fileInfo), + }; + + // Add file extension for files + if (fileType === Gio.FileType.REGULAR) { + let fileExtension = fileName.split('.').pop(); + item.type = `${fileExtension}`; + } + + contents.push(item); + contents.sort((a, b) => { + const aIsFolder = a.type.startsWith('folder'); + const bIsFolder = b.type.startsWith('folder'); + if (aIsFolder && !bIsFolder) { + return -1; + } else if (!aIsFolder && bIsFolder) { + return 1; + } else { + return a.name.localeCompare(b.name); // Sort alphabetically within folders and files + } + }); + } + } catch (e) { + if (!silent) console.log(e); + } + return contents; +} diff --git a/config/ags/modules/overview/overview_hyprland.js b/config/ags/modules/overview/overview_hyprland.js new file mode 100644 index 00000000..034d6c81 --- /dev/null +++ b/config/ags/modules/overview/overview_hyprland.js @@ -0,0 +1,423 @@ +// TODO +// - Make client destroy/create not destroy and recreate the whole thing +// - Active ws hook optimization: only update when moving to next group +// +const { Gdk, Gtk } = imports.gi; +const { Gravity } = imports.gi.Gdk; +import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../variables.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; + +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +const { execAsync, exec } = Utils; +import { setupCursorHoverGrab } from '../.widgetutils/cursorhover.js'; +import { dumpToWorkspace, swapWorkspace } from "./actions.js"; +import { substitute } from "../.miscutils/icons.js"; + +const NUM_OF_WORKSPACES_SHOWN = userOptions.overview.numOfCols * userOptions.overview.numOfRows; +const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; +const POPUP_CLOSE_TIME = 100; // ms + +const overviewTick = Variable(false); + +export default () => { + const clientMap = new Map(); + let workspaceGroup = 0; + const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({ + label: `${label}`, + setup: (menuItem) => { + let submenu = new Gtk.Menu(); + submenu.className = 'menu'; + + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + const startWorkspace = offset + 1; + const endWorkspace = startWorkspace + NUM_OF_WORKSPACES_SHOWN - 1; + for (let i = startWorkspace; i <= endWorkspace; i++) { + let button = new Gtk.MenuItem({ + label: `Workspace ${i}` + }); + button.connect("activate", () => { + // execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print); + actionFunc(thisWorkspace, i); + overviewTick.setValue(!overviewTick.value); + }); + submenu.append(button); + } + menuItem.set_reserve_indicator(true); + menuItem.set_submenu(submenu); + } + }) + + const Window = ({ address, at: [x, y], size: [w, h], workspace: { id, name }, class: c, title, xwayland }, screenCoords) => { + const revealInfoCondition = (Math.min(w, h) * userOptions.overview.scale > 70); + if (w <= 0 || h <= 0 || (c === '' && title === '') || c.endsWith('-dropterm')) return null; + // Non-primary monitors + if (screenCoords.x != 0) x -= screenCoords.x; + if (screenCoords.y != 0) y -= screenCoords.y; + // Other offscreen adjustments + if (x + w <= 0) x += (Math.floor(x / SCREEN_WIDTH) * SCREEN_WIDTH); + else if (x < 0) { w = x + w; x = 0; } + if (y + h <= 0) x += (Math.floor(y / SCREEN_HEIGHT) * SCREEN_HEIGHT); + else if (y < 0) { h = y + h; y = 0; } + // Truncate if offscreen + if (x + w > SCREEN_WIDTH) w = SCREEN_WIDTH - x; + if (y + h > SCREEN_HEIGHT) h = SCREEN_HEIGHT - y; + + const appIcon = Widget.Icon({ + icon: substitute(c), + size: Math.min(w, h) * userOptions.overview.scale / 2.5, + }); + return Widget.Button({ + attribute: { + address, x, y, w, h, ws: id, + updateIconSize: (self) => { + appIcon.size = Math.min(self.attribute.w, self.attribute.h) * userOptions.overview.scale / 2.5; + }, + }, + className: 'overview-tasks-window', + hpack: 'start', + vpack: 'start', + css: ` + margin-left: ${Math.round(x * userOptions.overview.scale)}px; + margin-top: ${Math.round(y * userOptions.overview.scale)}px; + margin-right: -${Math.round((x + w) * userOptions.overview.scale)}px; + margin-bottom: -${Math.round((y + h) * userOptions.overview.scale)}px; + `, + onClicked: (self) => { + App.closeWindow('overview'); + Utils.timeout(POPUP_CLOSE_TIME, () => Hyprland.messageAsync(`dispatch focuswindow address:${address}`)); + }, + onMiddleClickRelease: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`), + onSecondaryClick: (button) => { + button.toggleClassName('overview-tasks-window-selected', true); + const menu = Widget.Menu({ + className: 'menu', + children: [ + Widget.MenuItem({ + child: Widget.Label({ + xalign: 0, + label: "Close (Middle-click)", + }), + onActivate: () => Hyprland.messageAsync(`dispatch closewindow address:${address}`), + }), + ContextMenuWorkspaceArray({ + label: "Dump windows to workspace", + actionFunc: dumpToWorkspace, + thisWorkspace: Number(id) + }), + ContextMenuWorkspaceArray({ + label: "Swap windows with workspace", + actionFunc: swapWorkspace, + thisWorkspace: Number(id) + }), + ], + }); + menu.connect("deactivate", () => { + button.toggleClassName('overview-tasks-window-selected', false); + }) + menu.connect("selection-done", () => { + button.toggleClassName('overview-tasks-window-selected', false); + }) + menu.popup_at_widget(button.get_parent(), Gravity.SOUTH, Gravity.NORTH, null); // Show menu below the button + button.connect("destroy", () => menu.destroy()); + }, + child: Widget.Box({ + homogeneous: true, + child: Widget.Box({ + vertical: true, + vpack: 'center', + className: 'spacing-v-5', + children: [ + appIcon, + // TODO: Add xwayland tag instead of just having italics + Widget.Revealer({ + transition: 'slide_down', + revealChild: revealInfoCondition, + child: Widget.Label({ + maxWidthChars: 10, // Doesn't matter what number + truncate: 'end', + className: `${xwayland ? 'txt txt-italic' : 'txt'}`, + css: ` + font-size: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale / 14.6}px; + margin: 0px ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale / 10}px; + `, + // If the title is too short, include the class + label: (title.length <= 1 ? `${c}: ${title}` : title), + }) + }) + ] + }) + }), + tooltipText: `${c}: ${title}`, + setup: (button) => { + setupCursorHoverGrab(button); + + button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE); + button.drag_source_set_icon_name(substitute(c)); + // button.drag_source_set_icon_gicon(icon); + + button.connect('drag-begin', (button) => { // On drag start, add the dragging class + button.toggleClassName('overview-tasks-window-dragging', true); + }); + button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address + data.set_text(address, address.length); + button.toggleClassName('overview-tasks-window-dragging', false); + }); + }, + }); + } + + const Workspace = (index) => { + // const fixed = Widget.Fixed({ + // attribute: { + // put: (widget, x, y) => { + // fixed.put(widget, x, y); + // }, + // move: (widget, x, y) => { + // fixed.move(widget, x, y); + // }, + // } + // }); + const fixed = Widget.Box({ + attribute: { + put: (widget, x, y) => { + if (!widget.attribute) return; + // Note: x and y are already multiplied by userOptions.overview.scale + const newCss = ` + margin-left: ${Math.round(x)}px; + margin-top: ${Math.round(y)}px; + margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px; + margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px; + `; + widget.css = newCss; + fixed.pack_start(widget, false, false, 0); + }, + move: (widget, x, y) => { + if (!widget) return; + if (!widget.attribute) return; + // Note: x and y are already multiplied by userOptions.overview.scale + const newCss = ` + margin-left: ${Math.round(x)}px; + margin-top: ${Math.round(y)}px; + margin-right: -${Math.round(x + (widget.attribute.w * userOptions.overview.scale))}px; + margin-bottom: -${Math.round(y + (widget.attribute.h * userOptions.overview.scale))}px; + `; + widget.css = newCss; + }, + } + }) + const WorkspaceNumber = ({ index, ...rest }) => Widget.Label({ + className: 'overview-tasks-workspace-number', + label: `${index}`, + css: ` + margin: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * userOptions.overview.scale * userOptions.overview.wsNumMarginScale}px; + font-size: ${SCREEN_HEIGHT * userOptions.overview.scale * userOptions.overview.wsNumScale}px; + `, + setup: (self) => self.hook(Hyprland.active.workspace, (self) => { + // Update when going to new ws group + const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN); + self.label = `${currentGroup * NUM_OF_WORKSPACES_SHOWN + index}`; + }), + ...rest, + }) + const widget = Widget.Box({ + className: 'overview-tasks-workspace', + vpack: 'center', + css: ` + min-width: ${SCREEN_WIDTH * userOptions.overview.scale}px; + min-height: ${SCREEN_HEIGHT * userOptions.overview.scale}px; + `, + children: [Widget.EventBox({ + hexpand: true, + vexpand: true, + onPrimaryClick: () => { + App.closeWindow('overview'); + Utils.timeout(POPUP_CLOSE_TIME, () => Hyprland.messageAsync(`dispatch workspace ${index}`)); + }, + setup: (eventbox) => { + eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); + eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + Hyprland.messageAsync(`dispatch movetoworkspacesilent ${index + offset},address:${data.get_text()}`) + overviewTick.setValue(!overviewTick.value); + }); + }, + child: Widget.Overlay({ + child: Widget.Box({}), + overlays: [ + WorkspaceNumber({ index: index, hpack: 'start', vpack: 'start' }), + fixed + ] + }), + })], + }); + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + fixed.attribute.put(WorkspaceNumber(offset + index), 0, 0); + widget.clear = () => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + clientMap.forEach((client, address) => { + if (!client) return; + if ((client.attribute.ws <= offset || client.attribute.ws > offset + NUM_OF_WORKSPACES_SHOWN) || + (client.attribute.ws == offset + index)) { + client.destroy(); + client = null; + clientMap.delete(address); + } + }); + } + widget.set = (clientJson, screenCoords) => { + let c = clientMap.get(clientJson.address); + if (c) { + if (c.attribute?.ws !== clientJson.workspace.id) { + c.destroy(); + c = null; + clientMap.delete(clientJson.address); + } + else if (c) { + c.attribute.w = clientJson.size[0]; + c.attribute.h = clientJson.size[1]; + c.attribute.updateIconSize(c); + fixed.attribute.move(c, + Math.max(0, clientJson.at[0] * userOptions.overview.scale), + Math.max(0, clientJson.at[1] * userOptions.overview.scale) + ); + return; + } + } + const newWindow = Window(clientJson, screenCoords); + if (newWindow === null) return; + // clientMap.set(clientJson.address, newWindow); + fixed.attribute.put(newWindow, + Math.max(0, newWindow.attribute.x * userOptions.overview.scale), + Math.max(0, newWindow.attribute.y * userOptions.overview.scale) + ); + clientMap.set(clientJson.address, newWindow); + }; + widget.unset = (clientAddress) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + let c = clientMap.get(clientAddress); + if (!c) return; + c.destroy(); + c = null; + clientMap.delete(clientAddress); + }; + widget.show = () => { + fixed.show_all(); + } + return widget; + }; + + const arr = (s, n) => { + const array = []; + for (let i = 0; i < n; i++) + array.push(s + i); + + return array; + }; + + const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({ + children: arr(startWorkspace, workspaces).map(Workspace), + attribute: { + monitorMap: [], + getMonitorMap: (box) => { + execAsync('hyprctl -j monitors').then(monitors => { + box.attribute.monitorMap = JSON.parse(monitors).reduce((acc, item) => { + acc[item.id] = { x: item.x, y: item.y }; + return acc; + }, {}); + }); + }, + update: (box) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + if (!App.getWindow(windowName).visible) return; + Hyprland.messageAsync('j/clients').then(clients => { + const allClients = JSON.parse(clients); + const kids = box.get_children(); + kids.forEach(kid => kid.clear()); + for (let i = 0; i < allClients.length; i++) { + const client = allClients[i]; + const childID = client.workspace.id - (offset + startWorkspace); + if (offset + startWorkspace <= client.workspace.id && + client.workspace.id <= offset + startWorkspace + workspaces) { + const screenCoords = box.attribute.monitorMap[client.monitor]; + if (kids[childID]) { + kids[childID].set(client, screenCoords); + } + continue; + } + } + kids.forEach(kid => kid.show()); + }).catch(print); + }, + updateWorkspace: (box, id) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + if (!( // Not in range, ignore + offset + startWorkspace <= id && + id <= offset + startWorkspace + workspaces + )) return; + // if (!App.getWindow(windowName).visible) return; + Hyprland.messageAsync('j/clients').then(clients => { + const allClients = JSON.parse(clients); + const kids = box.get_children(); + for (let i = 0; i < allClients.length; i++) { + const client = allClients[i]; + if (client.workspace.id != id) continue; + const screenCoords = box.attribute.monitorMap[client.monitor]; + kids[id - (offset + startWorkspace)]?.set(client, screenCoords); + } + kids[id - (offset + startWorkspace)]?.show(); + }).catch(print); + }, + }, + setup: (box) => { + box.attribute.getMonitorMap(box); + box + .hook(overviewTick, (box) => box.attribute.update(box)) + .hook(Hyprland, (box, clientAddress) => { + const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN; + const kids = box.get_children(); + const client = Hyprland.getClient(clientAddress); + if (!client) return; + const id = client.workspace.id; + + box.attribute.updateWorkspace(box, id); + kids[id - (offset + startWorkspace)]?.unset(clientAddress); + }, 'client-removed') + .hook(Hyprland, (box, clientAddress) => { + const client = Hyprland.getClient(clientAddress); + if (!client) return; + box.attribute.updateWorkspace(box, client.workspace.id); + }, 'client-added') + .hook(Hyprland.active.workspace, (box) => { + // Full update when going to new ws group + const previousGroup = box.attribute.workspaceGroup; + const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN); + if (currentGroup !== previousGroup) { + box.attribute.update(box); + box.attribute.workspaceGroup = currentGroup; + } + }) + .hook(App, (box, name, visible) => { // Update on open + if (name == 'overview' && visible) box.attribute.update(box); + }) + }, + }); + + return Widget.Revealer({ + revealChild: true, + transition: 'slide_down', + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Box({ + vertical: true, + className: 'overview-tasks', + children: Array.from({ length: userOptions.overview.numOfRows }, (_, index) => + OverviewRow({ + startWorkspace: 1 + index * userOptions.overview.numOfCols, + workspaces: userOptions.overview.numOfCols, + }) + ) + }), + }); +} \ No newline at end of file diff --git a/config/ags/modules/overview/searchbuttons.js b/config/ags/modules/overview/searchbuttons.js new file mode 100644 index 00000000..f5892f31 --- /dev/null +++ b/config/ags/modules/overview/searchbuttons.js @@ -0,0 +1,163 @@ +const { Gtk } = imports.gi; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { execAsync, exec } = Utils; +import { searchItem } from './searchitem.js'; +import { execAndClose, couldBeMath, launchCustomCommand } from './miscfunctions.js'; + +export const DirectoryButton = ({ parentPath, name, type, icon }) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: 'Open', + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: userOptions.animations.durationSmall, + child: actionText, + }); + return Widget.Button({ + className: 'overview-search-result-btn', + onClicked: () => { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open '${parentPath}/${name}'`, `&`]).catch(print); + }, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Box({ + className: 'overview-search-results-icon', + homogeneous: true, + child: Widget.Icon({ + icon: icon, + }), + }), + Widget.Label({ + className: 'overview-search-results-txt txt txt-norm', + label: name, + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ] + }) + ] + }), + setup: (self) => self + .on('focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }) + .on('focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }) + , + }) +} + +export const CalculationResultButton = ({ result, text }) => searchItem({ + materialIconName: '󱖦 ', + name: `Math result`, + actionName: "Copy", + content: `${result}`, + onActivate: () => { + App.closeWindow('overview'); + execAsync(['wl-copy', `${result}`]).catch(print); + }, +}); + +export const DesktopEntryButton = (app) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: 'Launch', + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: userOptions.animations.durationSmall, + child: actionText, + }); + return Widget.Button({ + className: 'overview-search-result-btn', + onClicked: () => { + App.closeWindow('overview'); + app.launch(); + }, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Box({ + className: 'overview-search-results-icon', + homogeneous: true, + child: Widget.Icon({ + icon: app.iconName, + }), + }), + Widget.Label({ + className: 'overview-search-results-txt txt txt-norm', + label: app.name, + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ] + }) + ] + }), + setup: (self) => self + .on('focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }) + .on('focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }) + , + }) +} + +export const ExecuteCommandButton = ({ command, terminal = false }) => searchItem({ + materialIconName: `${terminal ? 'terminal' : ' '}`, + name: `Run command`, + actionName: `Execute ${terminal ? 'in terminal' : ''}`, + content: `${command}`, + onActivate: () => execAndClose(command, terminal), + extraClassName: 'techfont', +}) + +export const CustomCommandButton = ({ text = '' }) => searchItem({ + materialIconName: ' ', + name: 'Action', + actionName: 'Run', + content: `${text}`, + onActivate: () => { + App.closeWindow('overview'); + launchCustomCommand(text); + }, +}); + +export const SearchButton = ({ text = '' }) => searchItem({ + materialIconName: '󰜏 ', + name: 'Search the web', + actionName: 'Go', + content: `${text}`, + onActivate: () => { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open '${userOptions.search.engineBaseUrl}${text} ${['', ...userOptions.search.excludedSites].join(' -site:')}' &`]).catch(print); + }, +}); \ No newline at end of file diff --git a/config/ags/modules/overview/searchitem.js b/config/ags/modules/overview/searchitem.js new file mode 100644 index 00000000..2a3303a4 --- /dev/null +++ b/config/ags/modules/overview/searchitem.js @@ -0,0 +1,65 @@ +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; + +export const searchItem = ({ materialIconName, name, actionName, content, onActivate, extraClassName = '', ...rest }) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: userOptions.animations.durationLarge, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: `${actionName}`, + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: userOptions.animations.durationSmall, + child: actionText, + }) + return Widget.Button({ + className: `overview-search-result-btn txt ${extraClassName}`, + onClicked: onActivate, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Label({ + className: `icon-material overview-search-results-icon`, + label: `${materialIconName}`, + }), + Widget.Box({ + vertical: true, + children: [ + Widget.Label({ + hpack: 'start', + className: 'overview-search-results-txt txt-smallie txt-subtext', + label: `${name}`, + truncate: "end", + }), + Widget.Label({ + hpack: 'start', + className: 'overview-search-results-txt txt-norm', + label: `${content}`, + truncate: "end", + }), + ] + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ], + }) + ] + }), + setup: (self) => self + .on('focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }) + .on('focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }) + , + }); +} diff --git a/config/ags/modules/overview/windowcontent.js b/config/ags/modules/overview/windowcontent.js new file mode 100644 index 00000000..7a19dd3c --- /dev/null +++ b/config/ags/modules/overview/windowcontent.js @@ -0,0 +1,262 @@ +const { Gdk, Gtk } = imports.gi; +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; + +import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; +const { execAsync, exec } = Utils; +import { execAndClose, expandTilde, hasUnterminatedBackslash, couldBeMath, launchCustomCommand, ls } from './miscfunctions.js'; +import { + CalculationResultButton, CustomCommandButton, DirectoryButton, + DesktopEntryButton, ExecuteCommandButton, SearchButton +} from './searchbuttons.js'; +import { checkKeybind } from '../.widgetutils/keybind.js'; + +// Add math funcs +const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math; +const pi = Math.PI; +// trigonometric funcs for deg +const sind = x => sin(x * pi / 180); +const cosd = x => cos(x * pi / 180); +const tand = x => tan(x * pi / 180); +const cotd = x => cot(x * pi / 180); +const asind = x => asin(x) * 180 / pi; +const acosd = x => acos(x) * 180 / pi; +const atand = x => atan(x) * 180 / pi; +const acotd = x => acot(x) * 180 / pi; + +const MAX_RESULTS = 10; +const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size +const OVERVIEW_WS_NUM_SCALE = 0.0; +const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07; +const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; + +function iconExists(iconName) { + let iconTheme = Gtk.IconTheme.get_default(); + return iconTheme.has_icon(iconName); +} + +const OptionalOverview = async () => { + try { + return (await import('./overview_hyprland.js')).default(); + } catch { + return Widget.Box({}); + // return (await import('./overview_hyprland.js')).default(); + } +}; + +const overviewContent = await OptionalOverview(); + +export const SearchAndWindows = () => { + var _appSearchResults = []; + + const ClickToClose = ({ ...props }) => Widget.EventBox({ + ...props, + onPrimaryClick: () => App.closeWindow('overview'), + onSecondaryClick: () => App.closeWindow('overview'), + onMiddleClick: () => App.closeWindow('overview'), + }); + const resultsBox = Widget.Box({ + className: 'overview-search-results', + vertical: true, + vexpand: true, + }); + const resultsRevealer = Widget.Revealer({ + transitionDuration: userOptions.animations.durationLarge, + revealChild: false, + transition: 'slide_down', + // duration: 200, + hpack: 'center', + child: resultsBox, + }); + const entryPromptRevealer = Widget.Revealer({ + transition: 'crossfade', + transitionDuration: userOptions.animations.durationLarge, + revealChild: true, + hpack: 'center', + child: Widget.Label({ + className: 'overview-search-prompt txt-small txt', + label: 'Type to search' + }), + }); + + const entryIconRevealer = Widget.Revealer({ + transition: 'crossfade', + transitionDuration: userOptions.animations.durationLarge, + revealChild: false, + hpack: 'end', + child: Widget.Label({ + className: 'txt txt-large icon-material overview-search-icon', + label: ' ', + }), + }); + + const entryIcon = Widget.Box({ + className: 'overview-search-prompt-box', + setup: box => box.pack_start(entryIconRevealer, true, true, 0), + }); + + const entry = Widget.Entry({ + className: 'overview-search-box txt-small txt', + hpack: 'center', + onAccept: (self) => { // This is when you hit Enter + const text = self.text; + if (text.length == 0) return; + const isAction = text.startsWith('>'); + const isDir = (['/', '~'].includes(entry.text[0])); + + if (couldBeMath(text)) { // Eval on typing is dangerous, this is a workaround + try { + const fullResult = eval(text.replace(/\^/g, "**")); + // copy + execAsync(['wl-copy', `${fullResult}`]).catch(print); + App.closeWindow('overview'); + return; + } catch (e) { + // console.log(e); + } + } + if (isDir) { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open "${expandTilde(text)}"`, `&`]).catch(print); + return; + } + if (_appSearchResults.length > 0) { + App.closeWindow('overview'); + _appSearchResults[0].launch(); + return; + } + else if (text[0] == '>') { // Custom commands + App.closeWindow('overview'); + launchCustomCommand(text); + return; + } + // Fallback: Execute command + if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { + if (text.startsWith('sudo')) + execAndClose(text, true); + else + execAndClose(text, false); + } + + else { + App.closeWindow('overview'); + execAsync(['bash', '-c', `xdg-open '${userOptions.search.engineBaseUrl}${text} ${['', ...userOptions.search.excludedSites].join(' -site:')}' &`]).catch(print); + } + }, + onChange: (entry) => { // this is when you type + const isAction = entry.text[0] == '>'; + const isDir = (['/', '~'].includes(entry.text[0])); + resultsBox.get_children().forEach(ch => ch.destroy()); + + // check empty if so then dont do stuff + if (entry.text == '') { + resultsRevealer.revealChild = false; + overviewContent.revealChild = true; + entryPromptRevealer.revealChild = true; + entryIconRevealer.revealChild = false; + entry.toggleClassName('overview-search-box-extended', false); + return; + } + const text = entry.text; + resultsRevealer.revealChild = true; + overviewContent.revealChild = false; + entryPromptRevealer.revealChild = false; + entryIconRevealer.revealChild = true; + entry.toggleClassName('overview-search-box-extended', true); + _appSearchResults = Applications.query(text); + + // Calculate + if (couldBeMath(text)) { // Eval on typing is dangerous; this is a small workaround. + try { + const fullResult = eval(text.replace(/\^/g, "**")); + resultsBox.add(CalculationResultButton({ result: fullResult, text: text })); + } catch (e) { + // console.log(e); + } + } + if (isDir) { + var contents = []; + contents = ls({ path: text, silent: true }); + contents.forEach((item) => { + resultsBox.add(DirectoryButton(item)); + }) + } + if (isAction) { // Eval on typing is dangerous, this is a workaround. + resultsBox.add(CustomCommandButton({ text: entry.text })); + } + // Add application entries + let appsToAdd = MAX_RESULTS; + _appSearchResults.forEach(app => { + if (appsToAdd == 0) return; + resultsBox.add(DesktopEntryButton(app)); + appsToAdd--; + }); + + // Fallbacks + // if the first word is an actual command + if (!isAction && !hasUnterminatedBackslash(text) && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { + resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') })); + } + + // Add fallback: search + resultsBox.add(SearchButton({ text: entry.text })); + resultsBox.show_all(); + }, + }); + return Widget.Box({ + vertical: true, + children: [ + ClickToClose({ // Top margin. Also works as a click-outside-to-close thing + child: Widget.Box({ + className: 'bar-height', + }) + }), + Widget.Box({ + hpack: 'center', + children: [ + entry, + Widget.Box({ + className: 'overview-search-icon-box', + setup: (box) => { + box.pack_start(entryPromptRevealer, true, true, 0) + }, + }), + entryIcon, + ] + }), + overviewContent, + resultsRevealer, + ], + setup: (self) => self + .hook(App, (_b, name, visible) => { + if (name == 'overview' && !visible) { + resultsBox.children = []; + entry.set_text(''); + } + }) + .on('key-press-event', (widget, event) => { // Typing + const keyval = event.get_keyval()[1]; + const modstate = event.get_state()[1]; + if (checkKeybind(event, userOptions.keybinds.overview.altMoveLeft)) + entry.set_position(Math.max(entry.get_position() - 1, 0)); + else if (checkKeybind(event, userOptions.keybinds.overview.altMoveRight)) + entry.set_position(Math.min(entry.get_position() + 1, entry.get_text().length)); + else if (checkKeybind(event, userOptions.keybinds.overview.deleteToEnd)) { + const text = entry.get_text(); + const pos = entry.get_position(); + const newText = text.slice(0, pos); + entry.set_text(newText); + entry.set_position(newText.length); + } + else if (!(modstate & Gdk.ModifierType.CONTROL_MASK)) { // Ctrl not held + if (keyval >= 32 && keyval <= 126 && widget != entry) { + Utils.timeout(1, () => entry.grab_focus()); + entry.set_text(entry.text + String.fromCharCode(keyval)); + entry.set_position(-1); + } + } + }) + , + }); +}; diff --git a/config/ags/user/style.css b/config/ags/user/style.css new file mode 100644 index 00000000..13410ea4 --- /dev/null +++ b/config/ags/user/style.css @@ -0,0 +1,197 @@ +*:not(popover) { + all: unset; +} + +@import '../../../.config/waybar/wallust/colors-waybar.css'; + +/* define some colors */ +@define-color border-color @color12; +@define-color border-color-alt @color9; +@define-color text-color rgba(255, 255, 255, 0.7); +@define-color noti-bg rgba(0, 0, 0, 0.4); +@define-color noti-bg-alt #111111; + +widget { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; +} + +.overview-window { + margin-top: 2.727rem; +} + +.overview-search-box { + transition: 300ms cubic-bezier(0, 0.55, 0.45, 1); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 4px solid @border-color; + border-left: 1px solid @border-color-alt; + border-right: 1px solid @border-color-alt; + border-bottom: 4px solid @border-color; + box-shadow: 0px 2px 3px alpha(@color12, 0.45); + margin: 0.476rem; + min-width: 13.636rem; + min-height: 3.409rem; + padding: 0rem 1.364rem; + padding-right: 2.864rem; + background-color: @noti-bg; + color: @text-color; + caret-color: inherit; + font-weight: bolder; +} +.overview-search-box selection { + background-color: @noti-bg; + color: @text-color; +} + +.overview-search-box-extended { + min-width: 25.909rem; + caret-color: #FDD9FD; +} + +.overview-search-prompt { + color: @text-color; +} + +.overview-search-icon { + margin: 0rem 1.023rem; +} + +.overview-search-prompt-box { + margin-left: -18.545rem; + margin-right: 0.544rem; +} + +.overview-search-icon-box { + margin-left: -18.545rem; + margin-right: 0.544rem; +} + +.overview-search-results { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 4px solid @border-color; + border-left: 1px solid @border-color-alt; + border-right: 1px solid @border-color-alt; + border-bottom: 4px solid @border-color; + box-shadow: 0px 2px 3px @color9; + margin: 0.476rem; + min-width: 28.773rem; + padding: 0.682rem; + background-color: @noti-bg; + color: @text-color; + font-weight: bold; +} + +.overview-search-results-icon { + margin: 0rem 0.682rem; + font-size: 2.386rem; + min-width: 2.386rem; + min-height: 2.386rem; +} + +.overview-search-results-txt { + margin-right: 0.682rem; +} + +.overview-search-results-txt-cmd { + margin-right: 0.682rem; + font-family: "JetBrains Mono NF", "JetBrains Mono Nerd Font", "JetBrains Mono NL", "SpaceMono NF", "SpaceMono Nerd Font", monospace; + font-size: 1.227rem; +} + +.overview-search-result-btn { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + padding: 0.341rem; + min-width: 2.386rem; + min-height: 2.386rem; + caret-color: transparent; +} + +.overview-search-result-btn:hover, +.overview-search-result-btn:focus { + background-color: alpha(@color7, 0.9); + color: alpha(@color0, 0.7); +} + +.overview-search-result-btn:active { + background-color: alpha(@color7, 0.9); + color: @color4; +} + +.overview-tasks { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 4px solid @border-color; + border-left: 1px solid @border-color-alt; + border-right: 1px solid @border-color-alt; + border-bottom: 4px solid @border-color; + box-shadow: 0px 2px 3px @color5; + margin: 0.476rem; + padding: 0.341rem; + /* background-color: rgba(49, 50, 68, 0.8); */ + background-color: @noti-bg; + color: #EBDFED; +} + +.overview-tasks-workspace { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + margin: 0.341rem; + /* background-color: #26233A; */ + background-image: url('../../rofi/.current_wallpaper'); + background-size: cover; + background-position: center; + border: 0.068rem solid alpha(@color4, 0.5); +} + +.overview-tasks-workspace-number { + font-family: "Open Sans", "Noto Sans", sans-serif; + color: #CFC2D3; +} + +.overview-tasks-window { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + transition: 300ms cubic-bezier(0.1, 1, 0, 1); + background-color: alpha(@color3, .7); + /* background-color: @color_a3; */ + /* background-color: rgba(46, 40, 50, 0.8); */ + color: #EBDFED; + border: 0.068rem solid @color7; +} + +.overview-tasks-window:hover, +.overview-tasks-window:focus { + background-color: alpha(@color9, 0.8); +} + +.overview-tasks-window:active { + background-color: alpha(@color9, 0.8); +} + +.overview-tasks-window-selected { + background-color: alpha(@color9, 0.8); +} + +.overview-tasks-window-dragging { + opacity: 0.2; +} + +.growingRadial { + transition: 300ms cubic-bezier(0.2, 0, 0, 1); +} + +.fadingRadial { + transition: 50ms cubic-bezier(0.2, 0, 0, 1); +} + +.sidebar-pinned { + margin: 0rem; + border-radius: 0rem; + border-bottom-right-radius: 1.705rem; + border: 0rem solid; +} + +/*# sourceMappingURL=style.css.map */ diff --git a/config/ags/user_options.js b/config/ags/user_options.js new file mode 100644 index 00000000..9105decb --- /dev/null +++ b/config/ags/user_options.js @@ -0,0 +1,21 @@ + +const userConfigOptions = { + // For every option, see ~/.config/ags/modules/.configuration/user_options.js + // (vscode users ctrl+click this: file://./modules/.configuration/user_options.js) + // (vim users: `:vsp` to split window, move cursor to this path, press `gf`. `Ctrl-w` twice to switch between) + // options listed in this file will override the default ones in the above file + // Here's an example + 'overview':{ + 'scale': 0.15, + 'numOfRows': 2 + }, + 'keybinds': { + 'sidebar': { + 'pin': "Ctrl+p", + 'nextTab': "Ctrl+Page_Down", + 'prevTab': "Ctrl+Page_Up", + }, + }, +} + +export default userConfigOptions; diff --git a/config/ags/variables.js b/config/ags/variables.js new file mode 100644 index 00000000..645a5807 --- /dev/null +++ b/config/ags/variables.js @@ -0,0 +1,21 @@ +const { Gtk } = imports.gi; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +const { exec, execAsync } = Utils; + +Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets/icons`); + +// Screen size +export const SCREEN_WIDTH = Number(exec(`bash -c "hyprctl monitors -j | jq '.[0].width / .[0].scale'"`)); +export const SCREEN_HEIGHT = Number(exec(`bash -c "hyprctl monitors -j | jq '.[0].height / .[0].scale'"`)); + +// Mode switching +export const currentShellMode = Variable('normal', {}) // normal, focus +globalThis['currentMode'] = currentShellMode; +globalThis['cycleMode'] = () => { + if (currentShellMode.value === 'normal') { + currentShellMode.value = 'focus'; + } else { + currentShellMode.value = 'normal'; + } +} diff --git a/config/hypr/UserConfigs/Startup_Apps.conf b/config/hypr/UserConfigs/Startup_Apps.conf index 63f2ee68..f8af55e4 100644 --- a/config/hypr/UserConfigs/Startup_Apps.conf +++ b/config/hypr/UserConfigs/Startup_Apps.conf @@ -26,6 +26,7 @@ exec-once = $scriptsDir/Polkit.sh # starup apps exec-once = nm-applet --indicator exec-once = swaync +#exec-once = ags #exec-once = blueman-applet #exec-once = rog-control-center exec-once = waybar diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index c92352fc..70202ba4 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -12,13 +12,14 @@ $UserScripts = $HOME/.config/hypr/UserScripts $UserConfigs = $HOME/.config/hypr/UserConfigs # settings for User defaults apps - set your default terminal and file manager on this file -source= $UserConfigs/01-UserDefaults.conf +source= $UserConfigs/01-UserDefaults.conf # common shortcuts #bindr = $mainMod, $mainMod_L, exec, pkill rofi || rofi -show drun -modi drun,filebrowser,run,window # Super Key to Launch rofi menu bind = $mainMod, D, exec, pkill rofi || true && rofi -show drun -modi drun,filebrowser,run,window # Main Menu (APP Launcher) bind = $mainMod, B, exec, xdg-open "https://" # default browser -bind = $mainMod, A, global, quickshell:overviewToggle # desktop overview (if installed) +#bind = $mainMod, A, exec, pkill rofi || true && ags -t 'overview' # desktop overview (if installed) +#bind = $mainMod, A, global, quickshell:overviewToggle # desktop overview (if installed) bind = $mainMod, Return, exec, $term #terminal bind = $mainMod, E, exec, $files #file manager diff --git a/copy.sh b/copy.sh index 481b3832..e2c06bc1 100755 --- a/copy.sh +++ b/copy.sh @@ -235,13 +235,37 @@ if command -v blueman-applet >/dev/null 2>&1; then sed -i '/^\s*#exec-once = blueman-applet/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf fi +# Check if ags is installed edit ags behaviour on configs +if command -v ags >/dev/null 2>&1; then + sed -i '/^\s*#exec-once = ags/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh + sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/Refresh.sh + + # Uncomment the ags overview keybind (tolerates spaces/tabs) + sed -i '/^\s*#\s*bind\s*=\s*\$mainMod,\s*A,\s*exec,\s*pkill rofi\s*\|\|\s*true\s*&&\s*ags\s*-t\s*'\''overview'\''/s/^\s*#\s*//' config/hypr/UserConfigs/UserKeybinds.conf + + # Comment the quickshell line if not already commented (tolerates spaces/tabs) + sed -i '/^\s*bind\s*=\s*\$mainMod,\s*A,\s*global,\s*quickshell:overviewToggle/{ + s/^\s*/#/ + }' config/hypr/UserConfigs/UserKeybinds.conf +fi + # Check if quickshell is installed edit quickshell behaviour on configs if command -v qs >/dev/null 2>&1; then sed -i '/^\s*#exec-once = qs/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/Refresh.sh + + # Uncomment the quickshell keybind line (tolerates spaces/tabs) + sed -i '/^\s*#\s*bind\s*=\s*\$mainMod,\s*A,\s*global,\s*quickshell:overviewToggle/s/^\s*#\s*//' config/hypr/UserConfigs/UserKeybinds.conf + + # Ensure the ags overview keybind is commented (tolerates spaces/tabs) + sed -i '/^\s*bind\s*=\s*\$mainMod,\s*A,\s*exec,\s*pkill rofi\s*\|\|\s*true\s*&&\s*ags\s*-t\s*'\''overview'\''/{ + s/^\s*/#/ + }' config/hypr/UserConfigs/UserKeybinds.conf fi + printf "\n%.0s" {1..1} # Checking if neovim or vim is installed and offer user if they want to make as default editor @@ -665,6 +689,42 @@ done printf "\n%.0s" {1..1} +# ags config +# Check if ags is installed +if command -v ags >/dev/null 2>&1; then + echo -e "${NOTE} - ${YELLOW}ags${RESET} is detected as installed" + + DIRPATH_AGS="$HOME/.config/ags" + + if [ ! -d "$DIRPATH_AGS" ]; then + echo "${INFO} - ags config not found, copying new config." + if [ -d "config/ags" ]; then + cp -r "config/ags/" "$DIRPATH_AGS" 2>&1 | tee -a "$LOG" + fi + else + read -p "${CAT} Do you want to overwrite your existing ${YELLOW}ags${RESET} config? [y/N] " answer_ags + case "$answer_ags" in + [Yy]* ) + BACKUP_DIR=$(get_backup_dirname) + mv "$DIRPATH_AGS" "$DIRPATH_AGS-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" + echo -e "${NOTE} - Backed up ags config to $DIRPATH_AGS-backup-$BACKUP_DIR" + + if cp -r "config/ags/" "$DIRPATH_AGS" 2>&1 | tee -a "$LOG"; then + echo "${OK} - ${YELLOW}ags configs${RESET} overwritten successfully." + else + echo "${ERROR} - Failed to copy ${YELLOW}ags${RESET} config." + exit 1 + fi + ;; + * ) + echo "${NOTE} - Skipping overwrite of ags config." + ;; + esac + fi +fi + +printf "\n%.0s" {1..1} + # quickshell (ags alternative) # Check if quickshell is installed if command -v qs >/dev/null 2>&1; then -- cgit v1.2.3 From 090139faea37a45bca4c9ad27661284410f12bef Mon Sep 17 00:00:00 2001 From: Lars Müller Date: Sat, 19 Jul 2025 14:46:56 +0200 Subject: add: message to rofi theme selector --- config/hypr/scripts/RofiThemeSelector.sh | 2 +- config/rofi/config-rofi-theme.rasi | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/RofiThemeSelector.sh b/config/hypr/scripts/RofiThemeSelector.sh index 8423b2fe..569b5739 100755 --- a/config/hypr/scripts/RofiThemeSelector.sh +++ b/config/hypr/scripts/RofiThemeSelector.sh @@ -125,7 +125,7 @@ while true; do rofi -dmenu -i \ -format 'i' \ -p "Rofi Theme" \ - -mesg "Preview: $(basename "$theme_to_preview_now" .rasi) | Enter: Preview | Ctrl+S: Apply & Exit | Esc: Cancel" \ + -mesg "Enter: Preview | Ctrl+S: Apply & Exit | Esc: Cancel" \ -config "$ROFI_THEME_FOR_THIS_SCRIPT" \ -selected-row "$current_selection_index" \ -kb-custom-1 "Control+s") diff --git a/config/rofi/config-rofi-theme.rasi b/config/rofi/config-rofi-theme.rasi index 652cd94c..0d77f857 100644 --- a/config/rofi/config-rofi-theme.rasi +++ b/config/rofi/config-rofi-theme.rasi @@ -11,8 +11,17 @@ window { /* ---- Mainbox ---- */ mainbox { - children: - [ "inputbar", "listview"]; + children: [ "inputbar", "message", "listview" ]; +} + +/* ---- Custombox ---- */ +/* Override custombox to ensure listview is visible for themes that use it */ +custombox { + spacing: 0px; + background-color: inherit; + text-color: inherit; + orientation: vertical; + children: [ "listview" ]; } /* ---- Entry input ---- */ @@ -42,3 +51,15 @@ element-text { margin: 0px; padding: 0px; } + +/* ---- Message ---- */ +message { + padding: 10px; + border-radius: 10px; + background-color: inherit; +} + +textbox { + text-color: inherit; + background-color: inherit; +} -- cgit v1.2.3 From 0d09e35428e645c4d28fe1ffc6acfa5c17c7965d Mon Sep 17 00:00:00 2001 From: "Ja.KooLit" <85185940+JaKooLit@users.noreply.github.com> Date: Sun, 20 Jul 2025 09:55:26 +0900 Subject: Update RofiThemeSelector.sh --- config/hypr/scripts/RofiThemeSelector.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config') diff --git a/config/hypr/scripts/RofiThemeSelector.sh b/config/hypr/scripts/RofiThemeSelector.sh index 569b5739..4c9d0e8f 100755 --- a/config/hypr/scripts/RofiThemeSelector.sh +++ b/config/hypr/scripts/RofiThemeSelector.sh @@ -125,7 +125,7 @@ while true; do rofi -dmenu -i \ -format 'i' \ -p "Rofi Theme" \ - -mesg "Enter: Preview | Ctrl+S: Apply & Exit | Esc: Cancel" \ + -mesg "‼️ **note** ‼️ Enter: Preview | Ctrl+S: Apply & Exit | Esc: Cancel" \ -config "$ROFI_THEME_FOR_THIS_SCRIPT" \ -selected-row "$current_selection_index" \ -kb-custom-1 "Control+s") -- cgit v1.2.3 From a3adc26702e4e0339f6a68c0465f38e2cddd9554 Mon Sep 17 00:00:00 2001 From: "Ja.KooLit" <85185940+JaKooLit@users.noreply.github.com> Date: Sun, 20 Jul 2025 09:58:35 +0900 Subject: Update RofiThemeSelector.sh updated to sort numerically and also added exclamation on message for better visibility --- config/hypr/scripts/RofiThemeSelector.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/RofiThemeSelector.sh b/config/hypr/scripts/RofiThemeSelector.sh index 4c9d0e8f..8b2fcb71 100755 --- a/config/hypr/scripts/RofiThemeSelector.sh +++ b/config/hypr/scripts/RofiThemeSelector.sh @@ -82,7 +82,7 @@ original_rofi_config_content_backup=$(cat "$ROFI_CONFIG_FILE") mapfile -t available_theme_names < <(( find "$ROFI_THEMES_DIR_CONFIG" -maxdepth 1 -name "*.rasi" -type f -printf "%f\n" 2>/dev/null find "$ROFI_THEMES_DIR_LOCAL" -maxdepth 1 -name "*.rasi" -type f -printf "%f\n" 2>/dev/null -) | sort -u) +) | sort -V -u) if [ ${#available_theme_names[@]} -eq 0 ]; then notify_user "$IDIR/error.png" "No Rofi Themes" "No .rasi files found in theme directories." @@ -125,7 +125,7 @@ while true; do rofi -dmenu -i \ -format 'i' \ -p "Rofi Theme" \ - -mesg "‼️ **note** ‼️ Enter: Preview | Ctrl+S: Apply & Exit | Esc: Cancel" \ + -mesg "‼️ **note** ‼️ Enter: Preview || Ctrl+S: Apply & Exit || Esc: Cancel" \ -config "$ROFI_THEME_FOR_THIS_SCRIPT" \ -selected-row "$current_selection_index" \ -kb-custom-1 "Control+s") -- cgit v1.2.3 From 465037373dcff849d8f90ec83e96522f71437527 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Sun, 20 Jul 2025 10:23:01 +0900 Subject: minor UserSettings update --- config/hypr/UserConfigs/UserSettings.conf | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'config') diff --git a/config/hypr/UserConfigs/UserSettings.conf b/config/hypr/UserConfigs/UserSettings.conf index 1f8c23c3..067a3810 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -8,8 +8,9 @@ # NOTE: some settings are in ~/.config/hypr/UserConfigs/UserDecorAnimations.conf dwindle { - pseudotile = yes - preserve_split = yes + pseudotile = true + preserve_split = true + #smart_split = true special_scale_factor = 0.8 } @@ -48,7 +49,7 @@ input { middle_button_emulation = true tap-to-click = true drag_lock = false - } + } # below for devices with touchdevice ie. touchscreen touchdevice { -- cgit v1.2.3 From ed1fe4992d44b05767a65731837d8cfa37f59230 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Mon, 21 Jul 2025 17:26:37 +0900 Subject: uploaded new script for the new sddm theme --- config/hypr/scripts/sddm_wallpaper-v2.sh | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 config/hypr/scripts/sddm_wallpaper-v2.sh (limited to 'config') diff --git a/config/hypr/scripts/sddm_wallpaper-v2.sh b/config/hypr/scripts/sddm_wallpaper-v2.sh new file mode 100644 index 00000000..898053c7 --- /dev/null +++ b/config/hypr/scripts/sddm_wallpaper-v2.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ +# SDDM Wallpaper and Wallust Colors Setter + +# for the upcoming changes on the simple_sddm_theme + +# variables +terminal=kitty +wallDIR="$HOME/Pictures/wallpapers" +SCRIPTSDIR="$HOME/.config/hypr/scripts" +wallpaper_current="$HOME/.config/hypr/wallpaper_effects/.wallpaper_current" +wallpaper_modified="$HOME/.config/hypr/wallpaper_effects/.wallpaper_modified" +sddm_simple="/usr/share/sddm/themes/simple_sddm_2" + +# rofi-wallust-sddm colors path +rofi_wallust="$HOME/.config/rofi/wallust/colors-rofi.rasi" +sddm_theme_conf="$sddm_simple/theme.conf" + +# Directory for swaync +iDIR="$HOME/.config/swaync/images" +iDIRi="$HOME/.config/swaync/icons" + +# Parse arguments +mode="effects" # default +if [[ "$1" == "--normal" ]]; then + mode="normal" +elif [[ "$1" == "--effects" ]]; then + mode="effects" +fi + + + +# Extract colors from rofi wallust config +get_color() { + grep -oP "$1:\s*\K#[A-Fa-f0-9]+" "$rofi_wallust" +} + +color11=$(get_color 'color11') +HeaderTextColor=$(get_color 'color12') +DateTextColor=$(get_color 'color13') +bg_color=$(get_color 'color0') +placeholder_color="$accent_color" +icon_color=$(get_color 'foreground') +color11=$(get_color 'color11') + +# Define an array of key-value pairs for SDDM config updates +declare -A color_updates=( + [HeaderTextColor]="$HeaderTextColor" + [DateTextColor]="$DateTextColor" + [TimeTextColor]="$DateTextColor" + [SystemButtonsIconsColor]="$DateTextColor" + [SessionButtonTextColor]="$DateTextColor" + [VirtualKeyboardButtonTextColor]="$DateTextColor" + [BackgroundColor]="$bg_color" + [PlaceholderTextColor]="$placeholder_color" + [IconColor]="$icon_color" + [DropdownBackgroundColor]="$bg_color" + [HighlightBackgroundColor]="$color11" +) + +# Apply changes via terminal and sudo +$terminal -e bash -c " +echo 'Enter your password to update SDDM wallpapers and colors'; +$(for key in "${!color_updates[@]}"; do + value="${color_updates[$key]}" + echo "sudo sed -i \"s/${key}=\\\"#.*\\\"/${key}=\\\"$value\\\"/\" \"$sddm_theme_conf\"" +done) +" + +# Set wallpaper path based on mode +wallpaper_path=$([[ "$mode" == "normal" ]] && echo "$wallpaper_current" || echo "$wallpaper_modified") + +# Copy wallpaper safely +if [[ -f "$wallpaper_path" ]]; then + sudo cp "$wallpaper_path" "$sddm_simple/Backgrounds/default" + notify-send -i "$iDIR/ja.png" "SDDM" "Background SET" +else + notify-send -i "$iDIR/ja.png" "SDDM" "Wallpaper not found: $wallpaper_path" + echo "Error: Wallpaper file not found: $wallpaper_path" + exit 1 +fi + -- cgit v1.2.3 From 54af0ed84c6efcf61f088ab1af1188842aa4ed0d Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 22 Jul 2025 00:13:48 +0900 Subject: updated copy.sh to work with simple_sddm_2 and updated wallpaper_sddm script --- config/hypr/scripts/sddm_wallpaper-v2.sh | 82 +++++++++++++++----------------- copy.sh | 2 +- 2 files changed, 39 insertions(+), 45 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/sddm_wallpaper-v2.sh b/config/hypr/scripts/sddm_wallpaper-v2.sh index 898053c7..19fe7158 100644 --- a/config/hypr/scripts/sddm_wallpaper-v2.sh +++ b/config/hypr/scripts/sddm_wallpaper-v2.sh @@ -28,55 +28,49 @@ elif [[ "$1" == "--effects" ]]; then mode="effects" fi - - # Extract colors from rofi wallust config -get_color() { - grep -oP "$1:\s*\K#[A-Fa-f0-9]+" "$rofi_wallust" -} - -color11=$(get_color 'color11') -HeaderTextColor=$(get_color 'color12') -DateTextColor=$(get_color 'color13') -bg_color=$(get_color 'color0') -placeholder_color="$accent_color" -icon_color=$(get_color 'foreground') -color11=$(get_color 'color11') +color14=$(grep -oP 'color14:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color13=$(grep -oP 'color13:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color12=$(grep -oP 'color12:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color1=$(grep -oP 'color1:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color0=$(grep -oP 'color0:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color10=$(grep -oP 'color10:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +foreground=$(grep -oP 'foreground:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +#background-color=$(grep -oP 'background:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") -# Define an array of key-value pairs for SDDM config updates -declare -A color_updates=( - [HeaderTextColor]="$HeaderTextColor" - [DateTextColor]="$DateTextColor" - [TimeTextColor]="$DateTextColor" - [SystemButtonsIconsColor]="$DateTextColor" - [SessionButtonTextColor]="$DateTextColor" - [VirtualKeyboardButtonTextColor]="$DateTextColor" - [BackgroundColor]="$bg_color" - [PlaceholderTextColor]="$placeholder_color" - [IconColor]="$icon_color" - [DropdownBackgroundColor]="$bg_color" - [HighlightBackgroundColor]="$color11" -) +# wallpaper to use +if [[ "$mode" == "normal" ]]; then + wallpaper_path="$wallpaper_current" +else + wallpaper_path="$wallpaper_modified" +fi -# Apply changes via terminal and sudo +# Launch terminal and apply changes $terminal -e bash -c " echo 'Enter your password to update SDDM wallpapers and colors'; -$(for key in "${!color_updates[@]}"; do - value="${color_updates[$key]}" - echo "sudo sed -i \"s/${key}=\\\"#.*\\\"/${key}=\\\"$value\\\"/\" \"$sddm_theme_conf\"" -done) -" -# Set wallpaper path based on mode -wallpaper_path=$([[ "$mode" == "normal" ]] && echo "$wallpaper_current" || echo "$wallpaper_modified") +# Update the colors in the SDDM config +sudo sed -i \"s/HeaderTextColor=\\\"#.*\\\"/HeaderTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/DateTextColor=\\\"#.*\\\"/DateTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/TimeTextColor=\\\"#.*\\\"/TimeTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/DropdownSelectedBackgroundColor=\\\"#.*\\\"/DropdownSelectedBackgroundColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" -# Copy wallpaper safely -if [[ -f "$wallpaper_path" ]]; then - sudo cp "$wallpaper_path" "$sddm_simple/Backgrounds/default" - notify-send -i "$iDIR/ja.png" "SDDM" "Background SET" -else - notify-send -i "$iDIR/ja.png" "SDDM" "Wallpaper not found: $wallpaper_path" - echo "Error: Wallpaper file not found: $wallpaper_path" - exit 1 -fi +sudo sed -i \"s/SystemButtonsIconsColor=\\\"#.*\\\"/SystemButtonsIconsColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/SessionButtonTextColor=\\\"#.*\\\"/SessionButtonTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/VirtualKeyboardButtonTextColor=\\\"#.*\\\"/VirtualKeyboardButtonTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/HighlightBackgroundColor=\\\"#.*\\\"/HighlightBackgroundColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/LoginFieldTextColor=\\\"#.*\\\"/LoginFieldTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/PasswordFieldTextColor=\\\"#.*\\\"/PasswordFieldTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" + +sudo sed -i \"s/DropdownBackgroundColor=\\\"#.*\\\"/DropdownBackgroundColor=\\\"$color0\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/HighlightTextColor=\\\"#.*\\\"/HighlightTextColor=\\\"$color10\\\"/\" \"$sddm_theme_conf\" + +sudo sed -i \"s/PlaceholderTextColor=\\\"#.*\\\"/PlaceholderTextColor=\\\"$color14\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/UserIconColor=\\\"#.*\\\"/UserIconColor=\\\"$color14\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/PasswordIconColor=\\\"#.*\\\"/PasswordIconColor=\\\"$color14\\\"/\" \"$sddm_theme_conf\" + +# Copy wallpaper to SDDM theme +sudo cp \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default\" +notify-send -i \"$iDIR/ja.png\" \"SDDM\" \"Background SET\" +" \ No newline at end of file diff --git a/copy.sh b/copy.sh index 3677c86e..b3e001bd 100755 --- a/copy.sh +++ b/copy.sh @@ -406,7 +406,7 @@ while true; do # Applying to different SDDM themes apply_sddm_12h_format "/usr/share/sddm/themes/simple-sddm" - apply_sddm_12h_format "/usr/share/sddm/themes/simple-sddm-2" + apply_sddm_12h_format "/usr/share/sddm/themes/simple_sddm_2" # For SDDM (sequoia_2) sddm_directory_3="/usr/share/sddm/themes/sequoia_2" -- cgit v1.2.3 From badd7e251e526138840a5f513876453372c243ca Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 22 Jul 2025 01:15:43 +0900 Subject: added env = GSK_RENDERER,ngl for nvidia --- config/hypr/UserConfigs/ENVariables.conf | 1 + copy.sh | 2 ++ 2 files changed, 3 insertions(+) (limited to 'config') diff --git a/config/hypr/UserConfigs/ENVariables.conf b/config/hypr/UserConfigs/ENVariables.conf index 2f53c74c..f24cc306 100644 --- a/config/hypr/UserConfigs/ENVariables.conf +++ b/config/hypr/UserConfigs/ENVariables.conf @@ -54,6 +54,7 @@ env = ELECTRON_OZONE_PLATFORM_HINT,auto # auto selects Wayland if possible, X11 #env = LIBVA_DRIVER_NAME,nvidia #env = __GLX_VENDOR_LIBRARY_NAME,nvidia #env = NVD_BACKEND,direct +#env = GSK_RENDERER,ngl # additional ENV's for nvidia. Caution, activate with care #env = GBM_BACKEND,nvidia-drm diff --git a/copy.sh b/copy.sh index b3e001bd..5af67f21 100755 --- a/copy.sh +++ b/copy.sh @@ -87,6 +87,8 @@ if lspci -k | grep -A 2 -E "(VGA|3D)" | grep -iq nvidia; then sed -i '/env = LIBVA_DRIVER_NAME,nvidia/s/^#//' config/hypr/UserConfigs/ENVariables.conf sed -i '/env = __GLX_VENDOR_LIBRARY_NAME,nvidia/s/^#//' config/hypr/UserConfigs/ENVariables.conf sed -i '/env = NVD_BACKEND,direct/s/^#//' config/hypr/UserConfigs/ENVariables.conf + sed -i '/env = GSK_RENDERER,ngl/s/^#//' config/hypr/UserConfigs/ENVariables.conf + # no hardware cursors if nvidia detected sed -i 's/^\([[:space:]]*no_hardware_cursors[[:space:]]*=[[:space:]]*\)2/\1 1/' config/hypr/UserConfigs/UserSettings.conf #sed -i 's/^\([[:space:]]*explicit_sync[[:space:]]*=[[:space:]]*\)2/\1 0/' config/hypr/UserConfigs/UserSettings.conf -- cgit v1.2.3 From 894c6900c1e1c312b5097f35c15b59a407a991a9 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 22 Jul 2025 12:55:13 +0900 Subject: updated sddm-wallpaper script --- config/hypr/scripts/sddm_wallpaper-v2.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/sddm_wallpaper-v2.sh b/config/hypr/scripts/sddm_wallpaper-v2.sh index 19fe7158..a781156e 100644 --- a/config/hypr/scripts/sddm_wallpaper-v2.sh +++ b/config/hypr/scripts/sddm_wallpaper-v2.sh @@ -29,12 +29,13 @@ elif [[ "$1" == "--effects" ]]; then fi # Extract colors from rofi wallust config -color14=$(grep -oP 'color14:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") -color13=$(grep -oP 'color13:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") -color12=$(grep -oP 'color12:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") -color1=$(grep -oP 'color1:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") -color0=$(grep -oP 'color0:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") + +color0=$(grep -oP 'color1:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color1=$(grep -oP 'color0:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color7=$(grep -oP 'color14:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") color10=$(grep -oP 'color10:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color12=$(grep -oP 'color12:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") +color13=$(grep -oP 'color13:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") foreground=$(grep -oP 'foreground:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") #background-color=$(grep -oP 'background:\s*\K#[A-Fa-f0-9]+' "$rofi_wallust") @@ -54,20 +55,19 @@ sudo sed -i \"s/HeaderTextColor=\\\"#.*\\\"/HeaderTextColor=\\\"$color13\\\"/\" sudo sed -i \"s/DateTextColor=\\\"#.*\\\"/DateTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" sudo sed -i \"s/TimeTextColor=\\\"#.*\\\"/TimeTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" sudo sed -i \"s/DropdownSelectedBackgroundColor=\\\"#.*\\\"/DropdownSelectedBackgroundColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" - -sudo sed -i \"s/SystemButtonsIconsColor=\\\"#.*\\\"/SystemButtonsIconsColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" -sudo sed -i \"s/SessionButtonTextColor=\\\"#.*\\\"/SessionButtonTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" -sudo sed -i \"s/VirtualKeyboardButtonTextColor=\\\"#.*\\\"/VirtualKeyboardButtonTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/SystemButtonsIconsColor=\\\"#.*\\\"/SystemButtonsIconsColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/SessionButtonTextColor=\\\"#.*\\\"/SessionButtonTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/VirtualKeyboardButtonTextColor=\\\"#.*\\\"/VirtualKeyboardButtonTextColor=\\\"$color13\\\"/\" \"$sddm_theme_conf\" sudo sed -i \"s/HighlightBackgroundColor=\\\"#.*\\\"/HighlightBackgroundColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" sudo sed -i \"s/LoginFieldTextColor=\\\"#.*\\\"/LoginFieldTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" sudo sed -i \"s/PasswordFieldTextColor=\\\"#.*\\\"/PasswordFieldTextColor=\\\"$color12\\\"/\" \"$sddm_theme_conf\" -sudo sed -i \"s/DropdownBackgroundColor=\\\"#.*\\\"/DropdownBackgroundColor=\\\"$color0\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/DropdownBackgroundColor=\\\"#.*\\\"/DropdownBackgroundColor=\\\"$color1\\\"/\" \"$sddm_theme_conf\" sudo sed -i \"s/HighlightTextColor=\\\"#.*\\\"/HighlightTextColor=\\\"$color10\\\"/\" \"$sddm_theme_conf\" -sudo sed -i \"s/PlaceholderTextColor=\\\"#.*\\\"/PlaceholderTextColor=\\\"$color14\\\"/\" \"$sddm_theme_conf\" -sudo sed -i \"s/UserIconColor=\\\"#.*\\\"/UserIconColor=\\\"$color14\\\"/\" \"$sddm_theme_conf\" -sudo sed -i \"s/PasswordIconColor=\\\"#.*\\\"/PasswordIconColor=\\\"$color14\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/PlaceholderTextColor=\\\"#.*\\\"/PlaceholderTextColor=\\\"$color7\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/UserIconColor=\\\"#.*\\\"/UserIconColor=\\\"$color7\\\"/\" \"$sddm_theme_conf\" +sudo sed -i \"s/PasswordIconColor=\\\"#.*\\\"/PasswordIconColor=\\\"$color7\\\"/\" \"$sddm_theme_conf\" # Copy wallpaper to SDDM theme sudo cp \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default\" -- cgit v1.2.3 From 1cf593996c75e0cbb563648538ab6e3b3b85355d Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Tue, 22 Jul 2025 16:38:15 +0900 Subject: Added Colorful Waybar Stolen Style --- config/waybar/style/[Colorful] stolen-style.css | 348 ++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 config/waybar/style/[Colorful] stolen-style.css (limited to 'config') diff --git a/config/waybar/style/[Colorful] stolen-style.css b/config/waybar/style/[Colorful] stolen-style.css new file mode 100644 index 00000000..c6b3345f --- /dev/null +++ b/config/waybar/style/[Colorful] stolen-style.css @@ -0,0 +1,348 @@ +/* ----------- 💫 https://github.com/JaKooLit 💫 -------- */ +/* ....[Colorful] Stolen ......... */ +* { + font-family: "JetBrainsMono Nerd Font"; + font-weight: bold; + min-height: 0; + /* set font-size to 100% if font scaling is set to 1.00 using nwg-look */ + font-size: 97%; + font-feature-settings: '"zero", "ss01", "ss02", "ss03", "ss04", "ss05", "cv31"'; +} + +window#waybar { + transition-property: background-color; + transition-duration: 0.5s; + background: rgba(0, 0, 0, 0.8); + border-radius: 6px; +} + +window#waybar.hidden { + opacity: 0.2; +} + +window#waybar.empty, +window#waybar.empty #window { + background-color: rgba(0, 0, 0, 0.8); + padding: 0px; + border: 0px; +} + +#backlight, +#backlight-slider, +#battery, +#bluetooth, +#clock, +#cpu, +#disk, +#idle_inhibitor, +#keyboard-state, +#memory, +#mode, +#mpris, +#network, +#power-profiles-daemon, +#pulseaudio, +#pulseaudio-slider, +#taskbar button, +#taskbar, +#temperature, +#tray, +#window, +#wireplumber, +#workspaces, +#custom-backlight, +#custom-cycle_wall, +#custom-github, +#custom-hint, +#custom-hyprWindowMode, +#custom-keyboard, +#custom-light_dark, +#custom-lock, +#custom-menu, +#custom-power, +#custom-power_vertical, +#custom-speaker, +#custom-swaync, +#custom-updater, +#custom-weather, +#custom-weather.clearNight, +#custom-weather.cloudyFoggyDay, +#custom-weather.cloudyFoggyNight, +#custom-weather.default, +#custom-weather.rainyDay, +#custom-weather.rainyNight, +#custom-weather.severe, +#custom-weather.showyIcyDay, +#custom-weather.snowyIcyNight, +#custom-weather.sunnyDay { + padding-top: 2px; + padding-bottom: 2px; + padding-right: 6px; + padding-left: 6px; +} + +#workspaces button { + padding-left: 10px; + padding-right: 10px; + animation: gradient_f 20s ease-in infinite; + transition: all 0.1s linear; +} + +#workspaces button.persistent { + color: #faba4a; +} + +#workspaces button.empty { + color: #ffffed; +} + +#workspaces button.active { + color: #00ffff; + border: 2px solid #455a64; + border-radius: 16px; +} + +#workspaces button.urgent { + color: #f7768e; + border: 2px solid #f7768e; + border-radius: 16px; +} + +#workspaces button:hover { + border: 2px solid #455a64; + border-radius: 16px; +} + +#idle_inhibitor { + color: #7aa2f7; +} + +#backlight { + color: #7aa2f7; +} + +#bluetooth { + color: #7aa2f7; +} + +#clock { + color: #9fe044; + padding-right: 7px; +} + +#custom-swaync { + color: #9fe044; + border: 2px solid #455a64; + border-radius: 20px; + padding-left: 13px; + padding-right: 12px; +} + +#custom-keyboard { + color: #e0af68; + border: 2px solid #455a64; + border-radius: 20px; + padding-left: 14px; + padding-right: 14px; +} + +#custom-cycle_wall { + color: #c0caf5; + padding-left: 7px; + padding-right: 4px; +} + +#custom-hint { + color: #c0caf5; + padding-left: 0px; + padding-right: 4px; +} + +#custom-github { + color: #c0caf5; + padding-left: 0px; + padding-right: 4px; +} + +#custom-hyprWindowMode { + color: #c0caf5; + padding-left: 6px; +} + +#custom-niflveil { + color: #7dcfff; + padding-left: 0px; + padding-right: 10px; +} + +#custom-niflveil.empty { + color: #c0caf5; + +} + +#cpu { + padding-left: 10px; + padding-right: 3px; + color: #ffffed; + border-top: 2px solid #455a64; + border-bottom: 2px solid #455a64; + border-left: 2px solid #455a64; + border-radius: 20px 0px 0px 20px; +} + +#memory { + color: #ffffed; + padding-right: 8px; + border-bottom: 2px solid #455a64; + border-top: 2px solid #455a64; + border-radius: 0px; +} + +#temperature { + color: #ffffed; + border-bottom: 2px solid #455a64; + border-top: 2px solid #455a64; + border-radius: 0px; +} + +#disk { + padding-right: 16px; + color: #ffffed; + border-top: 2px solid #455a64; + border-bottom: 2px solid #455a64; + border-right: 2px solid #455a64; + border-radius: 0px 20px 20px 0px; +} + +#temperature.critical { + background-color: #f7768e; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; +} + +#keyboard-state { + color: #c7a9ff; +} + +#custom-cava_mviz { + color: #c7a9ff; +} + +#custom-menu { + + color: #7dcfff; + padding-left: 14px; + padding-right: 12px; +} + +#custom-power { + + color: #f7768e; + padding-left: 8px; + padding-right: 17px; +} + +#custom-updater { + color: #f7768e; + border: 2px solid #455a64; + border-radius: 20px; + padding-left: 10pt; + padding-right: 12px; + +} + +#custom-light_dark { + color: #7aa2f7; +} + +#custom-weather { + color: #9fe044; + +} + +#custom-speaker { + color: #faba4a; +} + +#pulseaudio { + color: #faba4a; + +} + +#pulseaudio.bluetooth { + color: #c7a9ff; +} + +#pulseaudio.muted { + color: #f7768e; +} + +#window { + font-size: 100%; + color: #ffffed; +} + +#mpris { + color: #bb9af7; +} + +#network { + color: #7dcfff; +} + +#network.disconnected, +#network.disabled { + background-color: #c0caf5; + color: #000000; +} + +#pulseaudio-slider slider { + min-width: 0px; + min-height: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; +} + +#pulseaudio-slider trough { + min-width: 80px; + min-height: 5px; + border-radius: 5px; +} + +#pulseaudio-slider highlight { + min-height: 10px; + border-radius: 5px; +} + +#backlight-slider slider { + min-width: 0px; + min-height: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; +} + +#backlight-slider trough { + min-width: 80px; + min-height: 10px; + border-radius: 5px; +} + +#backlight-slider highlight { + min-width: 10px; + border-radius: 5px; +} + +#custom-separator { + padding-left: 6px; + color: #f7768e; +} -- cgit v1.2.3 From ff66dd634ca3c61c26d342b8c1f0ffeb5fbc74d1 Mon Sep 17 00:00:00 2001 From: JaKooLit Date: Fri, 25 Jul 2025 19:29:04 +0900 Subject: added power profiles daemon on most configs --- config/waybar/configs/[BOT & Left] SouthWest | 3 ++- config/waybar/configs/[BOT & Right] SouthEast | 3 ++- config/waybar/configs/[BOT] Camellia | 1 + config/waybar/configs/[BOT] Chrysanthemum | 1 + config/waybar/configs/[BOT] Gardenia | 3 ++- config/waybar/configs/[BOT] Sleek | 1 + config/waybar/configs/[TOP & BOT] SummitSplit | 3 ++- config/waybar/configs/[TOP] Camellia | 1 + config/waybar/configs/[TOP] Peony | 3 ++- config/waybar/configs/[TOP] Sleek | 1 + 10 files changed, 15 insertions(+), 5 deletions(-) (limited to 'config') diff --git a/config/waybar/configs/[BOT & Left] SouthWest b/config/waybar/configs/[BOT & Left] SouthWest index 9bbe10cd..a039f040 100644 --- a/config/waybar/configs/[BOT & Left] SouthWest +++ b/config/waybar/configs/[BOT & Left] SouthWest @@ -42,7 +42,8 @@ "battery", "backlight", "pulseaudio", - //"wireplumber", + //"wireplumber", + "power-profiles-daemon", "pulseaudio#microphone", "keyboard-state", "custom/power", diff --git a/config/waybar/configs/[BOT & Right] SouthEast b/config/waybar/configs/[BOT & Right] SouthEast index f08fb507..9a58e952 100644 --- a/config/waybar/configs/[BOT & Right] SouthEast +++ b/config/waybar/configs/[BOT & Right] SouthEast @@ -42,7 +42,8 @@ "battery", "backlight", "pulseaudio", - //"wireplumber", + //"wireplumber", + "power-profiles-daemon", "pulseaudio#microphone", "keyboard-state", "custom/power", diff --git a/config/waybar/configs/[BOT] Camellia b/config/waybar/configs/[BOT] Camellia index 30eba9c6..f0a52329 100644 --- a/config/waybar/configs/[BOT] Camellia +++ b/config/waybar/configs/[BOT] Camellia @@ -41,6 +41,7 @@ "backlight/slider", "custom/speaker", "pulseaudio/slider", + "power-profiles-daemon", "battery", "clock#3", "network"], diff --git a/config/waybar/configs/[BOT] Chrysanthemum b/config/waybar/configs/[BOT] Chrysanthemum index ca10079b..3bc401c9 100644 --- a/config/waybar/configs/[BOT] Chrysanthemum +++ b/config/waybar/configs/[BOT] Chrysanthemum @@ -36,6 +36,7 @@ "modules-right": [ "pulseaudio#1", "backlight#2", + "power-profiles-daemon", "battery"], } \ No newline at end of file diff --git a/config/waybar/configs/[BOT] Gardenia b/config/waybar/configs/[BOT] Gardenia index 48c59e34..42355cac 100644 --- a/config/waybar/configs/[BOT] Gardenia +++ b/config/waybar/configs/[BOT] Gardenia @@ -36,7 +36,8 @@ "modules-right": [ "pulseaudio#1", - "backlight#2", + "backlight#2", + "power-profiles-daemon", "battery" ], diff --git a/config/waybar/configs/[BOT] Sleek b/config/waybar/configs/[BOT] Sleek index c5177d86..0dda9b35 100644 --- a/config/waybar/configs/[BOT] Sleek +++ b/config/waybar/configs/[BOT] Sleek @@ -39,6 +39,7 @@ "custom/separator#blank_2", "pulseaudio", "custom/separator#blank", + "power-profiles-daemon", "custom/power", ], diff --git a/config/waybar/configs/[TOP & BOT] SummitSplit b/config/waybar/configs/[TOP & BOT] SummitSplit index 35298c39..03c8e81b 100644 --- a/config/waybar/configs/[TOP & BOT] SummitSplit +++ b/config/waybar/configs/[TOP & BOT] SummitSplit @@ -23,7 +23,8 @@ "margin-right": 8, "modules-left": [ - "cpu", + "cpu", + "power-profiles-daemon", "temperature", "memory", "disk", diff --git a/config/waybar/configs/[TOP] Camellia b/config/waybar/configs/[TOP] Camellia index 5157c052..c93e9079 100644 --- a/config/waybar/configs/[TOP] Camellia +++ b/config/waybar/configs/[TOP] Camellia @@ -41,6 +41,7 @@ "backlight/slider", "custom/speaker", "pulseaudio/slider", + "power-profiles-daemon", "battery", "clock#3", "network"], diff --git a/config/waybar/configs/[TOP] Peony b/config/waybar/configs/[TOP] Peony index cf890324..2fd1dfe3 100644 --- a/config/waybar/configs/[TOP] Peony +++ b/config/waybar/configs/[TOP] Peony @@ -42,7 +42,8 @@ "pulseaudio", "custom/separator#blank", "temperature", - "custom/separator#blank", + "custom/separator#blank", + "group/mobo_drawer", "network"], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Sleek b/config/waybar/configs/[TOP] Sleek index 0f46198a..fe0f41ba 100644 --- a/config/waybar/configs/[TOP] Sleek +++ b/config/waybar/configs/[TOP] Sleek @@ -38,6 +38,7 @@ "custom/separator#blank_2", "pulseaudio", "custom/separator#blank", + "group/mobo_drawer", "custom/power", ], } -- cgit v1.2.3 From 7ed2aa6c81627626339c48a37be2249ce4ff54bf Mon Sep 17 00:00:00 2001 From: jteijema Date: Fri, 25 Jul 2025 13:12:24 +0200 Subject: fix: resolve emoji removal --- config/hypr/scripts/WaybarLayout.sh | 5 +++-- config/hypr/scripts/WaybarStyles.sh | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'config') diff --git a/config/hypr/scripts/WaybarLayout.sh b/config/hypr/scripts/WaybarLayout.sh index 955432fc..b4d9c493 100755 --- a/config/hypr/scripts/WaybarLayout.sh +++ b/config/hypr/scripts/WaybarLayout.sh @@ -29,9 +29,10 @@ main() { # Mark and locate the active layout default_row=0 + MARKER="👉" for i in "${!options[@]}"; do if [[ "${options[i]}" == "$current_name" ]]; then - options[i]="👉 ${options[i]}" + options[i]="$MARKER ${options[i]}" default_row=$i break fi @@ -49,7 +50,7 @@ main() { [[ -z "$choice" ]] && { echo "No option selected. Exiting."; exit 0; } # Strip marker before applying - choice=${choice% ⮕} + choice=${choice# $MARKER} case "$choice" in "no panel") diff --git a/config/hypr/scripts/WaybarStyles.sh b/config/hypr/scripts/WaybarStyles.sh index 20f0873b..a439f8eb 100755 --- a/config/hypr/scripts/WaybarStyles.sh +++ b/config/hypr/scripts/WaybarStyles.sh @@ -31,9 +31,10 @@ main() { # mark the active style and record its index default_row=0 + MARKER="👉" for i in "${!options[@]}"; do if [[ "${options[i]}" == "$current_name" ]]; then - options[i]="👉 ${options[i]}" + options[i]="$MARKER ${options[i]}" default_row=$i break fi @@ -50,7 +51,7 @@ main() { [[ -z "$choice" ]] && { echo "No option selected. Exiting."; exit 0; } # remove annotation and apply - choice=${choice% ⮕} + choice=${choice# $MARKER} apply_style "$choice" } -- cgit v1.2.3