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:
Nexoniarz 2026-08-30 16:51:39 +02:00
commit d1ff0a11e0
9 changed files with 224 additions and 2 deletions

View file

@ -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"

View file

@ -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

View 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