mirror of
https://github.com/Nexoniarz/nexoniarz-config-nixos.git
synced 2026-11-03 04:24:27 +00:00
Add Qt/GTK2 theming, default apps, LibreOffice; fix display.conf race
- Add qt-theme-set: a working Qt theming pipeline via qt6ct (the QT_QPA_PLATFORMTHEME=qt6ct env var was pointing at a platform theme plugin that wasn't even installed, so every Qt app fell back to its own default style). Also regenerates kdeglobals' KColorScheme groups and sets QT_QUICK_CONTROLS_STYLE=org.kde.desktop for Kirigami-based KDE apps, whose QML-drawn chrome ignores QPalette/qt6ct entirely. Gwenview's dialogs/content now follow the theme; its toolbar/statusbar chrome is a known remaining gap tied to Kirigami's platform-theme detection outside a full Plasma session - Add GTK2 compat (~/.gtkrc-2.0: icon theme + font follow the theme; no dark variant exists for GTK2's Adwaita engine) - Add modules/default-apps.nix: Gwenview/Ark as system-default image viewer/archive manager via /etc/xdg/mimeapps.list, LibreOffice (Qt6 build), and a working Thunar "Open Terminal Here" (it shipped calling exo-open, which isn't installed and never resolved) - Fix display-set.sh: mktemp defaulted to /tmp, a different filesystem than ~/.config/hypr here, making the mv into display.conf a non-atomic copy+unlink. Hyprland's file watcher could catch it mid-replace and throw "source= globbing error: found no match" on any monitor mode/orientation change. Stress-tested with 60 concurrent write+reload cycles after the fix with zero errors
This commit is contained in:
parent
1f5f9795bc
commit
d1ff0a11e0
9 changed files with 224 additions and 2 deletions
65
modules/default-apps.nix
Normal file
65
modules/default-apps.nix
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
environment.systemPackages = with pkgs; [
|
||||
libreoffice-qt6 # Qt6-styled build — fits the Qt theming work in theme.nix
|
||||
# better than the default GTK3 build would.
|
||||
];
|
||||
|
||||
# System-level fallback defaults (XDG spec: only consulted for a
|
||||
# mimetype the user's own ~/.config/mimeapps.list doesn't already
|
||||
# claim), so a future "Open With" choice always wins over this without
|
||||
# needing to touch it. Mimetype lists taken directly from Gwenview's
|
||||
# and Ark's own .desktop files, not guessed.
|
||||
#
|
||||
# One mimetype=desktopfile pair per line: the semicolon only separates
|
||||
# multiple desktop-file candidates for the SAME key, it doesn't chain
|
||||
# separate mimetype=value pairs on one line — mimeapps.list parsers
|
||||
# (verified against xdg-utils' own xdg-mime script) match by scanning
|
||||
# for a line that STARTS WITH "mimetype=", so cramming several pairs
|
||||
# onto one semicolon-joined line silently hides every key after the
|
||||
# first from ever being found.
|
||||
environment.etc."xdg/mimeapps.list".text =
|
||||
let
|
||||
gwenviewMimes = [
|
||||
"inode/directory" "image/avif" "image/gif" "image/heif" "image/jpeg"
|
||||
"image/jxl" "image/png" "image/bmp" "image/x-eps" "image/x-icns"
|
||||
"image/x-ico" "image/x-portable-bitmap" "image/x-portable-graymap"
|
||||
"image/x-portable-pixmap" "image/x-xbitmap" "image/x-xpixmap"
|
||||
"image/tiff" "image/x-psd" "image/x-webp" "image/webp" "image/x-tga"
|
||||
"image/x-xcf" "image/openraster" "image/svg+xml" "image/svg+xml-compressed"
|
||||
];
|
||||
arkMimes = [
|
||||
"application/x-cd-image" "application/x-bcpio" "application/x-cpio"
|
||||
"application/x-cpio-compressed" "application/x-rpm" "application/x-compress"
|
||||
"application/gzip" "application/x-bzip" "application/x-bzip2"
|
||||
"application/x-lzma" "application/x-xz" "application/zstd" "application/x-lz4"
|
||||
"application/x-source-rpm" "application/vnd.debian.binary-package"
|
||||
"application/vnd.ms-cab-compressed" "application/x-archive" "application/x-tar"
|
||||
"application/x-compressed-tar" "application/x-bzip-compressed-tar"
|
||||
"application/x-bzip2-compressed-tar" "application/x-tarz"
|
||||
"application/x-xz-compressed-tar" "application/x-lzma-compressed-tar"
|
||||
"application/x-7z-compressed" "application/vnd.rar" "application/zip"
|
||||
"application/x-java-archive" "application/x-arj"
|
||||
];
|
||||
line = desktop: mime: "${mime}=${desktop}";
|
||||
in
|
||||
''
|
||||
[Default Applications]
|
||||
${lib.concatMapStringsSep "\n" (line "org.kde.gwenview.desktop") gwenviewMimes}
|
||||
${lib.concatMapStringsSep "\n" (line "org.kde.ark.desktop") arkMimes}
|
||||
'';
|
||||
|
||||
# Thunar's own "Open Terminal Here" custom action (Edit > Configure
|
||||
# Custom Actions) shipped pointing at `exo-open --launch
|
||||
# TerminalEmulator` — exo isn't installed on this system at all (no
|
||||
# XFCE session, so its helpers.rc mechanism has nothing to resolve
|
||||
# against), so the action silently did nothing. `f`, not `w`: only
|
||||
# creates this baseline for a fresh account — once it exists, further
|
||||
# edits made through Thunar's own UI (adding more custom actions) are
|
||||
# never overwritten by a rebuild.
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /home/nexoniarz/.config/Thunar 0755 nexoniarz users -"
|
||||
''f /home/nexoniarz/.config/Thunar/uca.xml 0644 nexoniarz users - <?xml version="1.0" encoding="UTF-8"?><actions><action><icon>utilities-terminal</icon><name>Open Terminal Here</name><submenu></submenu><unique-id>1788014825097143-1</unique-id><command>kitty --working-directory %f</command><description>Open a terminal in this folder</description><range></range><patterns>*</patterns><startup-notify/><directories/></action></actions>''
|
||||
];
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ let
|
|||
displaySet = pkgs.writeShellScriptBin "display-set" (builtins.readFile ./scripts/display-set.sh);
|
||||
hyprThemeSet = pkgs.writeShellScriptBin "hypr-theme-set" (builtins.readFile ./scripts/hypr-theme-set.sh);
|
||||
gtkThemeSet = pkgs.writeShellScriptBin "gtk-theme-set" (builtins.readFile ./scripts/gtk-theme-set.sh);
|
||||
qtThemeSet = pkgs.writeShellScriptBin "qt-theme-set" (builtins.readFile ./scripts/qt-theme-set.sh);
|
||||
kittyThemeSet = pkgs.writeShellScriptBin "kitty-theme-set" (builtins.readFile ./scripts/kitty-theme-set.sh);
|
||||
rofiThemeSet = pkgs.writeShellScriptBin "rofi-theme-set" (builtins.readFile ./scripts/rofi-theme-set.sh);
|
||||
listCursorThemes = pkgs.writeShellScriptBin "list-cursor-themes" (builtins.readFile ./scripts/list-cursor-themes.sh);
|
||||
|
|
@ -36,6 +37,7 @@ in
|
|||
displaySet
|
||||
hyprThemeSet
|
||||
gtkThemeSet
|
||||
qtThemeSet
|
||||
kittyThemeSet
|
||||
rofiThemeSet
|
||||
listCursorThemes
|
||||
|
|
|
|||
|
|
@ -25,7 +25,15 @@ touch "$conf"
|
|||
|
||||
line="monitor = $descriptor"
|
||||
if grep -q "^monitor = $mon," "$conf" 2>/dev/null; then
|
||||
tmp=$(mktemp)
|
||||
# mktemp in the same directory as $conf, not the default /tmp: /tmp is
|
||||
# a different filesystem here, which makes `mv` below a non-atomic
|
||||
# copy+unlink instead of a rename — Hyprland's file watcher (it
|
||||
# watches every `source=`d file, including this one) could catch the
|
||||
# destination mid-replace and its `source=` glob would find nothing,
|
||||
# throwing "globbing error: found no match" (observed live after an
|
||||
# orientation change, which always hits this branch since DP-1
|
||||
# already has an existing line by then).
|
||||
tmp=$(mktemp "$conf.XXXXXX")
|
||||
awk -v prefix="monitor = $mon," -v newline="$line" '
|
||||
index($0, prefix) == 1 { print newline; next } { print }
|
||||
' "$conf" > "$tmp"
|
||||
|
|
|
|||
|
|
@ -117,3 +117,16 @@ if command -v dconf >/dev/null 2>&1; then
|
|||
dconf write /org/gnome/desktop/interface/icon-theme "'$icon_theme'"
|
||||
dconf write /org/gnome/desktop/interface/color-scheme "'$color_scheme'"
|
||||
fi
|
||||
|
||||
# GTK2 compat: separate config format (~/.gtkrc-2.0, not settings.ini),
|
||||
# and no live theme-switching mechanism at all — GTK2 apps only read this
|
||||
# at their own startup. Only icon theme and font actually follow the
|
||||
# current theme; gnome-themes-extra's bundled GTK2 Adwaita engine has no
|
||||
# dark variant to switch to (GTK2 predates that concept), so gtk-theme-
|
||||
# name here is always the one real option, not a broken attempt at dark
|
||||
# mode.
|
||||
cat > "$HOME/.gtkrc-2.0" <<EOF
|
||||
gtk-theme-name="Adwaita"
|
||||
gtk-icon-theme-name="$icon_theme"
|
||||
gtk-font-name="Noto Sans 10"
|
||||
EOF
|
||||
|
|
|
|||
105
modules/scripts/qt-theme-set.sh
Normal file
105
modules/scripts/qt-theme-set.sh
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env bash
|
||||
# Regenerates qt6ct's config + color scheme to match the current Quickshell
|
||||
# theme, the Qt-side equivalent of gtk-theme-set. Qt apps (Gwenview, Ark,
|
||||
# Kate, ...) had no working platform theme plugin at all before this — the
|
||||
# QT_QPA_PLATFORMTHEME=qt6ct env var (hyprland.conf) was pointing at a
|
||||
# plugin that wasn't even installed, so every Qt app fell back to
|
||||
# whatever default style/palette it shipped with, ignoring the rest of
|
||||
# the desktop entirely. style=Fusion (not a KDE/Breeze style) matters
|
||||
# specifically: Fusion paints straight from QPalette, so a custom color
|
||||
# scheme here is guaranteed to actually show — Breeze-family styles
|
||||
# prefer reading colors from kdeglobals instead, which is a separate,
|
||||
# static, unrelated leftover file on this machine.
|
||||
set -euo pipefail
|
||||
|
||||
accent="${1:?usage: qt-theme-set <accent> <bg> <bgAlt> <fg> <fgDim> <border> <dark:1|0>}"
|
||||
bg="${2:?bg required}"
|
||||
bg_alt="${3:?bgAlt required}"
|
||||
fg="${4:?fg required}"
|
||||
fg_dim="${5:?fgDim required}"
|
||||
border="${6:?border required}"
|
||||
dark="${7:?dark required}"
|
||||
|
||||
dir="$HOME/.config/qt6ct"
|
||||
mkdir -p "$dir/colors"
|
||||
|
||||
# qt6ct color scheme files store QPalette::ColorRole values as a fixed-
|
||||
# order, comma-separated #aarrggbb list — order and format verified
|
||||
# against the example schemes qt6ct itself ships
|
||||
# (share/qt6ct/colors/darker.conf etc.), not guessed: WindowText, Button,
|
||||
# Light, Midlight, Dark, Mid, Text, BrightText, ButtonText, Base, Window,
|
||||
# Shadow, Highlight, HighlightedText, Link, LinkVisited, AlternateBase,
|
||||
# NoRole, ToolTipBase, ToolTipText, PlaceholderText.
|
||||
a() { printf '#ff%s' "${1#\#}"; } # opaque
|
||||
t() { printf '#80%s' "${1#\#}"; } # translucent, for PlaceholderText
|
||||
|
||||
active="$(a "$fg"), $(a "$bg_alt"), $(a "$border"), $(a "$bg_alt"), $(a "$bg"), $(a "$border"), $(a "$fg"), $(a "$fg"), $(a "$fg"), $(a "$bg"), $(a "$bg"), $(a "$border"), $(a "$accent"), $(a "$bg"), $(a "$accent"), $(a "$accent"), $(a "$bg_alt"), $(a "$bg"), $(a "$bg_alt"), $(a "$fg"), $(t "$fg_dim")"
|
||||
disabled="$(a "$fg_dim"), $(a "$bg_alt"), $(a "$border"), $(a "$bg_alt"), $(a "$bg"), $(a "$border"), $(a "$fg_dim"), $(a "$fg_dim"), $(a "$fg_dim"), $(a "$bg"), $(a "$bg"), $(a "$border"), $(a "$accent"), $(a "$fg_dim"), $(a "$accent"), $(a "$accent"), $(a "$bg_alt"), $(a "$bg"), $(a "$bg_alt"), $(a "$fg_dim"), $(t "$fg_dim")"
|
||||
|
||||
color_file="$dir/colors/quickshell-theme.conf"
|
||||
cat > "$color_file" <<EOF
|
||||
[ColorScheme]
|
||||
active_colors=$active
|
||||
disabled_colors=$disabled
|
||||
inactive_colors=$active
|
||||
EOF
|
||||
|
||||
icon_theme="Gruvbox-Plus-Light"
|
||||
[ "$dark" = "1" ] && icon_theme="Gruvbox-Plus-Dark"
|
||||
|
||||
cat > "$dir/qt6ct.conf" <<EOF
|
||||
[Appearance]
|
||||
style=Fusion
|
||||
icon_theme=$icon_theme
|
||||
custom_palette=true
|
||||
color_scheme_path=$color_file
|
||||
standard_dialogs=default
|
||||
EOF
|
||||
|
||||
# qt6ct's palette only reaches classic QWidget UI. Modern KDE apps
|
||||
# (confirmed live: Gwenview) draw their toolbar/chrome with Kirigami/QML
|
||||
# instead, which ignores QPalette entirely and reads kdeglobals' color
|
||||
# groups directly via KColorScheme — so without this, qt6ct alone left
|
||||
# exactly that chrome stuck on whatever this machine's stale leftover
|
||||
# kdeglobals had (unrelated purple KDE colors from some earlier install),
|
||||
# while everything else (dialogs, the actual image viewport, ...)
|
||||
# correctly followed the theme.
|
||||
rgb() {
|
||||
local hex="${1#\#}"
|
||||
printf '%d,%d,%d' "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}"
|
||||
}
|
||||
|
||||
cat > "$HOME/.config/kdeglobals" <<EOF
|
||||
[General]
|
||||
ColorScheme=QuickshellTheme
|
||||
widgetStyle=Fusion
|
||||
|
||||
[KDE]
|
||||
widgetStyle=Fusion
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:View]
|
||||
BackgroundNormal=$(rgb "$bg")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundNormal=$(rgb "$accent")
|
||||
ForegroundNormal=$(rgb "$bg")
|
||||
DecorationFocus=$(rgb "$accent")
|
||||
DecorationHover=$(rgb "$accent")
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
EOF
|
||||
|
|
@ -13,6 +13,16 @@
|
|||
adwaita-icon-theme
|
||||
gruvbox-plus-icons
|
||||
dconf
|
||||
# Qt's side of the same live-theming pipeline as GTK above — see
|
||||
# qt-theme-set (modules/scripts/qt-theme-set.sh). Without this
|
||||
# installed, QT_QPA_PLATFORMTHEME=qt6ct (hyprland.conf) pointed at a
|
||||
# platform theme plugin that didn't exist, so every Qt app (Gwenview,
|
||||
# Ark, Kate, ...) silently fell back to its own default style/palette
|
||||
# instead of anything resembling the rest of the desktop.
|
||||
qt6Packages.qt6ct
|
||||
kdePackages.qqc2-desktop-style # QQC2 style needed for QT_QUICK_CONTROLS_STYLE=
|
||||
# org.kde.desktop (hyprland.conf) to actually
|
||||
# theme Kirigami-based apps' chrome
|
||||
];
|
||||
|
||||
# No environment.sessionVariables.GTK_THEME here on purpose: GTK_THEME
|
||||
|
|
@ -25,11 +35,16 @@
|
|||
systemd.tmpfiles.rules = [
|
||||
"d /home/nexoniarz/.config/gtk-3.0 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/.config/gtk-4.0 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/.config/qt6ct 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/.config/qt6ct/colors 0755 nexoniarz users -"
|
||||
# `f`, not `L+`: created empty only if missing, never overwritten on
|
||||
# rebuild — gtk-theme-set owns the actual content from here on.
|
||||
# rebuild — gtk-theme-set/qt-theme-set own the actual content from
|
||||
# here on.
|
||||
"f /home/nexoniarz/.config/gtk-3.0/gtk.css 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/gtk-4.0/gtk.css 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/gtk-3.0/settings.ini 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/gtk-4.0/settings.ini 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.gtkrc-2.0 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/qt6ct/qt6ct.conf 0644 nexoniarz users - -"
|
||||
];
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue