diff --git a/core/internal/matugen/matugen.go b/core/internal/matugen/matugen.go index e1c798a88..35ac11ebb 100644 --- a/core/internal/matugen/matugen.go +++ b/core/internal/matugen/matugen.go @@ -968,11 +968,32 @@ func applyKDEColorScheme(mode ColorMode) { } } +func gtkThemeInstalled(theme string) bool { + home, _ := os.UserHomeDir() + candidates := []string{ + filepath.Join(home, ".local/share/themes", theme), + filepath.Join(home, ".themes", theme), + filepath.Join("/usr/share/themes", theme), + filepath.Join("/usr/local/share/themes", theme), + } + for _, dir := range candidates { + if info, err := os.Stat(dir); err == nil && info.IsDir() { + return true + } + } + return false +} + func refreshGTKTheme(mode ColorMode) { + theme := mode.GTKTheme() + if !gtkThemeInstalled(theme) { + log.Infof("Skipping gtk-theme refresh: %s is not installed", theme) + return + } if err := utils.GsettingsSet("org.gnome.desktop.interface", "gtk-theme", ""); err != nil { log.Warnf("Failed to reset gtk-theme: %v", err) } - if err := utils.GsettingsSet("org.gnome.desktop.interface", "gtk-theme", mode.GTKTheme()); err != nil { + if err := utils.GsettingsSet("org.gnome.desktop.interface", "gtk-theme", theme); err != nil { log.Warnf("Failed to set gtk-theme: %v", err) } } diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index eccd3fc84..8516cf460 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -144,7 +144,7 @@ Singleton { Quickshell.execDetached(["mkdir", "-p", stateDir]); // shellDir may be an embedded-UI extraction, which is read-only and // unexecutable (dankgo shellapp/shellfs makeReadOnly chmods 0444) - Quickshell.execDetached(["bash", shellDir + "/scripts/gtk.sh", configDir, "", "", shellDir]); + Quickshell.execDetached(["bash", shellDir + "/scripts/gtk.sh", configDir, "assets", "", shellDir]); Proc.runCommand("matugenCheck", ["sh", "-c", "command -v matugen"], (output, code) => { matugenAvailable = (code === 0) && !envDisableMatugen; @@ -1955,7 +1955,13 @@ Singleton { function patchGtk3colors() { const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode); Proc.runCommand("gtk3Patcher", ["bash", shellDir + "/scripts/gtk.sh", configDir, "patch", isLight, shellDir], (output, exitCode) => { - if (exitCode !== 0) { + switch (exitCode) { + case 0: + refreshGtkTheme(); + break; + case 2: + break; + default: log.warn(`Failed to patch GTK3 colors: ${output}`); } }); @@ -2167,10 +2173,8 @@ Singleton { } if (!pendingThemeRequest) { - if (SettingsData.matugenTemplateGtk) { + if (SettingsData.matugenTemplateGtk) patchGtk3colors(); - refreshGtkTheme(); - } return; } diff --git a/quickshell/scripts/gtk.sh b/quickshell/scripts/gtk.sh index cb5ab705c..fa24fec47 100755 --- a/quickshell/scripts/gtk.sh +++ b/quickshell/scripts/gtk.sh @@ -1,14 +1,16 @@ #!/usr/bin/env bash CONFIG_DIR="$1" -# apply: setup symlinks and config dirs +# apply: setup overrides and config dirs # patch: refresh overrides in adw-gtk3 # remove: remove all overrides +# assets: relink adw-gtk3 assets if DMS already manages gtk.css MODE="${2:-apply}" IS_LIGHT="${3:-light}" +SHELL_DIR="$4" if [ -z "$CONFIG_DIR" ]; then - echo "Usage: $0 [apply|patch|remove] [is_light] [shell_dir]" >&2 + echo "Usage: $0 [apply|patch|remove|assets] [is_light] [shell_dir]" >&2 exit 1 fi @@ -33,6 +35,100 @@ get_adw_gtk3_dir() { echo "$target" } +# The light template references adw-gtk3 image assets relative to gtk.css +# (check/radio glyphs, slider knobs); without them checked boxes render as +# solid blocks. +link_gtk3_assets() { + local gtk3_dir="$1" + local assets_link="$gtk3_dir/assets" + + if [ -e "$assets_link" ] && [ ! -L "$assets_link" ]; then + echo "Leaving user-managed $assets_link in place" + return + fi + + local candidates=( + "$HOME/.local/share/themes/adw-gtk3/gtk-3.0/assets" + "$HOME/.themes/adw-gtk3/gtk-3.0/assets" + "/usr/share/themes/adw-gtk3/gtk-3.0/assets" + "/usr/local/share/themes/adw-gtk3/gtk-3.0/assets" + ) + local target="" + for c in "${candidates[@]}"; do + if [ -d "$c" ]; then + target="$c" + break + fi + done + if [ -z "$target" ] && [ -n "$SHELL_DIR" ] && [ -d "$SHELL_DIR/matugen/gtk3-assets" ]; then + target="$SHELL_DIR/matugen/gtk3-assets" + fi + if [ -z "$target" ]; then + return + fi + + ln -sfn "$target" "$assets_link" + echo "Linked GTK3 assets: $assets_link -> $target" +} + +DANK_IMPORT='@import url("dank-colors.css");' +DANK_IMPORT_RE='^@import url.*dank-colors\.css.*);$' + +# gtk.css is DMS-managed if it is our symlink or carries our import line. +# User-managed symlinks (e.g. home-manager) are never touched. +dms_managed_css() { + local gtk_css="$1" + if [ -L "$gtk_css" ]; then + case "$(readlink "$gtk_css")" in + *dank-colors.css*) return 0 ;; + *) return 1 ;; + esac + fi + [ -f "$gtk_css" ] && grep -q "$DANK_IMPORT_RE" "$gtk_css" +} + +inject_dank_import() { + local gtk_css="$1" + + if [ -L "$gtk_css" ]; then + if ! dms_managed_css "$gtk_css"; then + echo "Warning: '$gtk_css' is a user-managed symlink; leaving it untouched" >&2 + echo "Import dank-colors.css from your own stylesheet to use DMS colors" >&2 + return 1 + fi + rm "$gtk_css" + fi + + if [ -f "$gtk_css" ] && grep -q "$DANK_IMPORT_RE" "$gtk_css"; then + echo "Dank import already present in '$gtk_css'" + return 0 + fi + + if [ -f "$gtk_css" ] && [ -s "$gtk_css" ]; then + sed -i "1i\\$DANK_IMPORT" "$gtk_css" + else + echo "$DANK_IMPORT" >"$gtk_css" + fi + echo "Added dank-colors import to '$gtk_css'" +} + +remove_dank_import() { + local gtk_css="$1" + + if [ -L "$gtk_css" ]; then + if dms_managed_css "$gtk_css"; then + rm "$gtk_css" + echo "Removed DMS-managed symlink '$gtk_css'" + fi + return 0 + fi + + if [ -f "$gtk_css" ] && grep -q "$DANK_IMPORT_RE" "$gtk_css"; then + sed -i "/$DANK_IMPORT_RE/d" "$gtk_css" + echo "Removed dank-colors import from '$gtk_css'" + fi +} + remove_gtk3_patch() { local theme_dir="$1" local css_variant="$2" @@ -47,13 +143,14 @@ remove_gtk3_colors() { local gtk3_dir="$config_dir/gtk-3.0" # remove global override + remove_dank_import "$gtk3_dir/gtk.css" if [ ! -f "${gtk3_dir}/dank-colors.css" ]; then echo "Nothing to remove at '${gtk3_dir}'" else if rm "${gtk3_dir}/dank-colors.css"; then - echo "Removed GTK3 override form '${gtk3_dir}'" + echo "Removed GTK3 override from '${gtk3_dir}'" else - echo "Failed to removed GTK3 override from '${gtk3_dir}'" + echo "Failed to remove GTK3 override from '${gtk3_dir}'" fi fi @@ -61,9 +158,9 @@ remove_gtk3_colors() { for variant in light dark; do local adw_gtk3_dir && adw_gtk3_dir=$(get_adw_gtk3_dir "$variant") - if [ -z "$adw_gtk3_dir" ]; then - echo "Error: No version of adw-gtk3 ${variant} was found" >&2 - exit 1 + if [ -z "$adw_gtk3_dir" ] || [[ "$adw_gtk3_dir" =~ ^/usr ]]; then + echo "No user version of adw-gtk3 ${variant} found, nothing to unpatch" + continue fi for css_variant in light dark; do @@ -79,6 +176,10 @@ remove_gtk3_colors() { do_patch() { local theme_dir="$1" local variant="$2" + if [ -z "$theme_dir" ] || [[ "$theme_dir" =~ ^/usr ]]; then + echo "Skipping '$variant' patch: no user copy of adw-gtk3 for this variant" + return 0 + fi local css_variant="" [ "$variant" = "dark" ] && css_variant="-${variant}" if { @@ -102,14 +203,9 @@ patch_gtk3_colors() { [ "$is_light" = "false" ] && variant="dark" local adw_gtk3_dir && adw_gtk3_dir=$(get_adw_gtk3_dir "$variant") - if [ -z "$adw_gtk3_dir" ]; then - echo "Warning: No version of adw-gtk3 ${variant} was found" >&2 - exit 1 - fi - - if [[ "$adw_gtk3_dir" =~ ^/usr ]]; then - echo "Warning: No user version of adw-gtk3 ${variant} was found." >&2 - exit 1 + if [ -z "$adw_gtk3_dir" ] || [[ "$adw_gtk3_dir" =~ ^/usr ]]; then + echo "No user version of adw-gtk3 ${variant} was found, skipping patch" + exit 2 fi if [ ! -f "${gtk3_dir}/dank-colors.css" ]; then @@ -146,34 +242,23 @@ apply_gtk3_colors() { exit 1 fi - if [ -L "$gtk3_override" ]; then - rm "$gtk3_override" - elif [ -f "$gtk3_override" ]; then - mv "$gtk3_override" "$gtk3_override.backup.$(date +%s)" - echo "Backed up existing gtk.css" - fi - - ln -s "dank-colors.css" "$gtk3_override" - echo "Created symlink: $gtk3_override -> dank-colors.css" - + inject_dank_import "$gtk3_override" || exit 1 link_gtk3_assets "$gtk3_dir" return fi - # Else ensure there's no global override - if [ -L "$gtk3_override" ]; then - rm "$gtk3_override" - elif [ -f "$gtk3_override" ]; then - mv "$gtk3_override" "$gtk3_override.backup.$(date +%s)" - echo "Backed up and removed existing gtk.css" - fi + # adw-gtk3 carries the colors; ensure there's no DMS global override + remove_dank_import "$gtk3_override" - # Backup adw-gtk3 stylesheets + # Backup pristine adw-gtk3 stylesheets once for variant in light dark; do local adw_gtk3_dir && adw_gtk3_dir="$(get_adw_gtk3_dir "$variant")" - cp "$adw_gtk3_dir/gtk-3.0/gtk.css" "$adw_gtk3_dir/gtk-3.0/gtk.css.backup.$(date +%s)" - cp "$adw_gtk3_dir/gtk-3.0/gtk-dark.css" "$adw_gtk3_dir/gtk-3.0/gtk-dark.css.backup.$(date +%s)" + for css in gtk.css gtk-dark.css; do + if [ -f "$adw_gtk3_dir/$css" ] && [ ! -f "$adw_gtk3_dir/$css.dms-backup" ]; then + cp "$adw_gtk3_dir/$css" "$adw_gtk3_dir/$css.dms-backup" + fi + done done } @@ -184,6 +269,8 @@ remove_gtk4_colors() { local dank_colors="$gtk4_dir/dank-colors.css" local gtk_css="$gtk4_dir/gtk.css" + remove_dank_import "$gtk_css" + if [ ! -f "$dank_colors" ]; then echo "Nothing to remove in '$gtk4_dir'" return @@ -191,13 +278,6 @@ remove_gtk4_colors() { rm "$dank_colors" echo "Removed 'dank-colors.css' from '$gtk4_dir'" - - local gtk4_import="@import url(\"dank-colors.css\");" - if [ -f "$gtk_css" ] && grep -q '^@import url.*dank-colors\.css.*);$' "$gtk_css"; then - sed -i "/$gtk4_import/d" "$gtk_css" - echo "Removed gtk4 import in '$gtk_css'" - fi - } apply_gtk4_colors() { @@ -206,7 +286,6 @@ apply_gtk4_colors() { local gtk4_dir="$config_dir/gtk-4.0" local dank_colors="$gtk4_dir/dank-colors.css" local gtk_css="$gtk4_dir/gtk.css" - local gtk4_import="@import url(\"dank-colors.css\");" if [ ! -f "$dank_colors" ]; then echo "Error: GTK4 dank-colors.css not found at $dank_colors" >&2 @@ -214,21 +293,16 @@ apply_gtk4_colors() { exit 1 fi - if [ -f "$gtk_css" ] && grep -q '^@import url.*dank-colors\.css.*);$' "$gtk_css"; then - echo "GTK4 import already exists" - return - fi - - if [ -f "$gtk_css" ] && [ -s "$gtk_css" ]; then - sed -i "1i\\$gtk4_import" "$gtk_css" - else - echo "$gtk4_import" >"$gtk_css" - fi - echo "Updated GTK4 CSS import" + inject_dank_import "$gtk_css" || exit 1 } case "$MODE" in patch) + # Only refresh themes the user opted into via 'apply' + if ! dms_managed_css "$CONFIG_DIR/gtk-4.0/gtk.css"; then + echo "DMS GTK theming is not applied, skipping patch" + exit 2 + fi patch_gtk3_colors "$CONFIG_DIR" "$IS_LIGHT" echo "GTK3 colors patched successfully" ;; @@ -244,8 +318,14 @@ case "$MODE" in echo "GTK colors applied successfully" ;; + assets) + # Repair pass at startup; only acts when DMS already manages gtk.css + if dms_managed_css "$CONFIG_DIR/gtk-3.0/gtk.css"; then + link_gtk3_assets "$CONFIG_DIR/gtk-3.0" + fi + ;; *) - echo "Usage: $0 [apply|patch|remove] [is_light] [shell_dir]" >&2 + echo "Usage: $0 [apply|patch|remove|assets] [is_light] [shell_dir]" >&2 exit 1 ;; esac