diff --git a/CLAUDE.md b/CLAUDE.md index 244ae114..9c8354e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,6 +48,7 @@ This is a Quickshell-based desktop shell implementation with Material Design 3 d - **Quickshell Framework** - QML-based framework for building desktop shells - **Qt/QtQuick** - UI rendering and controls - **Wayland** - Display server protocol +- **Matugen** - Dynamic theming system for wallpaper-based colors and system app theming ## Development Commands @@ -162,6 +163,7 @@ shell.qml # Main entry point (minimal orchestration) - **AppLauncher**: Full-featured app grid/list with 93+ applications, search, categories - **ClipboardHistoryModal**: Complete clipboard management with cliphist integration - **TopBar**: Per-monitor panels with workspace switching, clock, system tray +- **System App Theming**: Automatic GTK and Qt application theming using matugen templates #### Key Widgets diff --git a/Common/Colors.qml b/Common/Colors.qml index 74271c05..24535fe8 100644 --- a/Common/Colors.qml +++ b/Common/Colors.qml @@ -14,8 +14,12 @@ Singleton { readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation) readonly property string homeDir: _homeUrl.startsWith("file://") ? _homeUrl.substring(7) : _homeUrl + readonly property string shellDir: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Common/", "") readonly property string wallpaperPath: Prefs.wallpaperPath property bool matugenAvailable: false + property bool gtkThemingEnabled: false + property bool qtThemingEnabled: false + property bool systemThemeGenerationInProgress: false property string matugenJson: "" property var matugenColors: ({ }) @@ -81,6 +85,8 @@ Singleton { Component.onCompleted: { console.log("Colors.qml → home =", homeDir); matugenCheck.running = true; + checkGtkThemingAvailability(); + checkQtThemingAvailability(); if (typeof Theme !== "undefined") Theme.isLightModeChanged.connect(root.onLightModeChanged); } @@ -165,6 +171,13 @@ Singleton { generateNiriConfig(); generateGhosttyConfig(); + + if (gtkThemingEnabled && typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) { + generateGtkThemes(); + } + if (qtThemingEnabled && typeof Prefs !== "undefined" && Prefs.qtThemingEnabled) { + generateQtThemes(); + } } function generateNiriConfig() { @@ -241,6 +254,49 @@ palette = 15=${fg_b}`; ghosttyConfigWriter.command = ["bash", "-c", `echo '${content}' > ghostty-colors.generated.conf`]; ghosttyConfigWriter.running = true; } + + function checkGtkThemingAvailability() { + gtkAvailabilityChecker.running = true; + } + + function checkQtThemingAvailability() { + qtAvailabilityChecker.running = true; + } + + function generateSystemThemes() { + if (systemThemeGenerationInProgress) { + console.log("System theme generation already in progress, skipping"); + return; + } + + if (!matugenAvailable) { + console.warn("Matugen not available, cannot generate system themes"); + return; + } + + if (!wallpaperPath || wallpaperPath === "") { + console.warn("No wallpaper path set, cannot generate system themes"); + return; + } + + console.log("Generating system themes using matugen templates"); + console.log("Wallpaper:", wallpaperPath); + console.log("Shell directory:", shellDir); + + systemThemeGenerationInProgress = true; + systemThemeGenerator.command = [shellDir + "/generate-themes.sh", wallpaperPath, shellDir]; + systemThemeGenerator.running = true; + } + + function generateGtkThemes() { + console.log("Generating GTK themes using matugen templates"); + generateSystemThemes(); + } + + function generateQtThemes() { + console.log("Generating Qt themes using matugen templates"); + generateSystemThemes(); + } Process { id: niriConfigWriter @@ -265,5 +321,104 @@ palette = 15=${fg_b}`; } } } + + Process { + id: gtkAvailabilityChecker + command: ["bash", "-c", "command -v gsettings >/dev/null && [ -d ~/.config/gtk-3.0 -o -d ~/.config/gtk-4.0 ]"] + running: false + onExited: (exitCode) => { + gtkThemingEnabled = (exitCode === 0); + console.log("GTK theming available:", gtkThemingEnabled); + } + } + + Process { + id: qtAvailabilityChecker + command: ["bash", "-c", "command -v qt5ct >/dev/null || command -v qt6ct >/dev/null"] + running: false + onExited: (exitCode) => { + qtThemingEnabled = (exitCode === 0); + console.log("Qt theming available:", qtThemingEnabled); + } + } + + Process { + id: systemThemeGenerator + running: false + + stdout: StdioCollector { + id: systemThemeStdout + } + + stderr: StdioCollector { + id: systemThemeStderr + } + + onStarted: { + console.log("System theme generation process started with command:", command); + } + + onExited: (exitCode) => { + systemThemeGenerationInProgress = false; + console.log("System theme generation process exited with code:", exitCode); + + if (exitCode === 0) { + console.log("System themes generated successfully"); + console.log("stdout:", systemThemeStdout.text); + + if (gtkThemingEnabled && typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) { + console.log("Applying GTK theme..."); + gtkThemeApplier.running = true; + } + + ToastService.showInfo("System themes updated successfully"); + } else { + console.error("System theme generation failed, exit code:", exitCode); + console.error("stdout:", systemThemeStdout.text); + console.error("stderr:", systemThemeStderr.text); + ToastService.showError("Failed to generate system themes: " + systemThemeStderr.text); + } + } + } + + Process { + id: gtkThemeApplier + running: false + + stdout: StdioCollector { + id: gtkApplierStdout + } + + stderr: StdioCollector { + id: gtkApplierStderr + } + + onExited: (exitCode) => { + if (exitCode === 0) { + console.log("GTK theme applied successfully"); + ToastService.showInfo("GTK applications themed successfully"); + } else { + console.warn("GTK theme application failed, exit code:", exitCode); + console.warn("stderr:", gtkApplierStderr.text); + ToastService.showWarning("GTK theme application failed"); + } + } + + Component.onCompleted: { + command = ["bash", "-c", ` + # Reset GTK theme first + gsettings set org.gnome.desktop.interface gtk-theme '' 2>/dev/null || true + # Apply adw-gtk3-dark theme + gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark 2>/dev/null || true + # Import the generated colors + if [ -f ~/.config/gtk-3.0/colors.css ]; then + echo "GTK 3 colors imported" + fi + if [ -f ~/.config/gtk-4.0/colors.css ]; then + echo "GTK 4 colors imported" + fi + `]; + } + } } diff --git a/Common/Prefs.qml b/Common/Prefs.qml index 3e77fd38..8ea81098 100644 --- a/Common/Prefs.qml +++ b/Common/Prefs.qml @@ -52,6 +52,8 @@ Singleton { property string fontFamily: "Inter Variable" property string monoFontFamily: "Fira Code" property int fontWeight: Font.Normal + property bool gtkThemingEnabled: false + property bool qtThemingEnabled: false readonly property string defaultFontFamily: "Inter Variable" readonly property string defaultMonoFontFamily: "Fira Code" @@ -131,6 +133,8 @@ Singleton { fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : defaultFontFamily; monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : defaultMonoFontFamily; fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal; + gtkThemingEnabled = settings.gtkThemingEnabled !== undefined ? settings.gtkThemingEnabled : false; + qtThemingEnabled = settings.qtThemingEnabled !== undefined ? settings.qtThemingEnabled : false; applyStoredTheme(); detectAvailableIconThemes(); detectQtTools(); @@ -182,7 +186,9 @@ Singleton { "doNotDisturb": doNotDisturb, "fontFamily": fontFamily, "monoFontFamily": monoFontFamily, - "fontWeight": fontWeight + "fontWeight": fontWeight, + "gtkThemingEnabled": gtkThemingEnabled, + "qtThemingEnabled": qtThemingEnabled }, null, 2)); } @@ -579,6 +585,16 @@ Singleton { saveSettings(); } + function setGtkThemingEnabled(enabled) { + gtkThemingEnabled = enabled; + saveSettings(); + } + + function setQtThemingEnabled(enabled) { + qtThemingEnabled = enabled; + saveSettings(); + } + // Helper to safely single-quote shell strings function _shq(s) { return "'" + String(s).replace(/'/g, "'\\''") + "'"; diff --git a/Modules/Settings/AppearanceTab.qml b/Modules/Settings/AppearanceTab.qml index b31a138c..11443ca1 100644 --- a/Modules/Settings/AppearanceTab.qml +++ b/Modules/Settings/AppearanceTab.qml @@ -755,12 +755,83 @@ ScrollView { ColorAnimation { duration: Theme.mediumDuration easing.type: Theme.standardEasing - } - } } + + + } + + } + + } + } + + // System App Theming Section + StyledRect { + width: parent.width + height: systemThemingSection.implicitHeight + Theme.spacingL * 2 + radius: Theme.cornerRadiusLarge + color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) + border.width: 1 + visible: Theme.isDynamicTheme && Colors.matugenAvailable + + Column { + id: systemThemingSection + + anchors.fill: parent + anchors.margins: Theme.spacingL + spacing: Theme.spacingM + + Row { + width: parent.width + spacing: Theme.spacingM + + DankIcon { + name: "extension" + size: Theme.iconSize + color: Theme.primary + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: "System App Theming" + font.pixelSize: Theme.fontSizeLarge + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + } + + DankToggle { + width: parent.width + text: "Theme GTK Applications" + description: Colors.gtkThemingEnabled ? "File managers, text editors, and system dialogs will match your theme" : "GTK theming not available (install gsettings and adw-gtk3)" + enabled: Colors.gtkThemingEnabled + checked: Colors.gtkThemingEnabled && Prefs.gtkThemingEnabled + onToggled: function(checked) { + Prefs.setGtkThemingEnabled(checked); + if (checked && Theme.isDynamicTheme) { + Colors.generateGtkThemes(); + } + } + } + + DankToggle { + width: parent.width + text: "Theme Qt Applications" + description: Colors.qtThemingEnabled ? "Qt applications will match your theme colors" : "Qt theming not available (install qt5ct or qt6ct)" + enabled: Colors.qtThemingEnabled + checked: Colors.qtThemingEnabled && Prefs.qtThemingEnabled + onToggled: function(checked) { + Prefs.setQtThemingEnabled(checked); + if (checked && Theme.isDynamicTheme) { + Colors.generateQtThemes(); + } + } } } diff --git a/README.md b/README.md index ee318a82..8a83466b 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,26 @@ paru -S quickshell-git |------------|---------|------------| | cava | Equalizer in TopBar uses Audio Data | Equalizer shifts at random | | cliphist | Allows clipboard history view | No clipboard history view available | -| matugen | Allows dynamic themes based on wallpaper | Just can choose from preconfigured themes instead of dynamic colors | +| matugen | Allows dynamic themes based on wallpaper and system app theming | Just can choose from preconfigured themes instead of dynamic colors | | ddcutil (or brightnessctl) | Allows controlling brightness of monitors | No Brightness | | wl-clipboard | Unlocks copy functionality of certain elements, such as process PIDs | No copy | -| qt5ct + qt6ct | Icon theme | Setting icon theme in settings won't work for QT5 or QT6 applications | +| qt5ct + qt6ct | Icon theme and Qt app theming | Setting icon theme in settings won't work for QT5 or QT6 applications, no Qt theming | +| adw-gtk3 | GTK app theming | No GTK theming | +| gsettings | GTK theme management | No GTK theming | ```bash # Arch -paru -S ttf-material-symbols-variable-git inter-font ttf-fira-code matugen cliphist cava wl-clipboard ddcutil +paru -S ttf-material-symbols-variable-git inter-font ttf-fira-code matugen cliphist cava wl-clipboard ddcutil adw-gtk3 qt5ct qt6ct ``` **Note on networking:** This shell requires NetworkManager for WiFi functionality. +**Note on system app theming:** DankMaterialShell can automatically theme GTK and Qt applications to match your dynamic wallpaper colors. This requires: +- For GTK apps: `adw-gtk3` theme and `gsettings` +- For Qt apps: `qt5ct` and/or `qt6ct` + +Enable these features in Settings → Appearance → System App Theming after installing the dependencies. + 3. Install DankMaterialShell ``` diff --git a/generate-themes.sh b/generate-themes.sh new file mode 100755 index 00000000..fb06858d --- /dev/null +++ b/generate-themes.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# System theme generation script for DankMaterialShell +# This script uses matugen to generate GTK and Qt themes from wallpaper + +WALLPAPER_PATH="$1" +SHELL_DIR="$2" + +if [ -z "$WALLPAPER_PATH" ] || [ -z "$SHELL_DIR" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +if [ ! -f "$WALLPAPER_PATH" ]; then + echo "Wallpaper file not found: $WALLPAPER_PATH" >&2 + exit 1 +fi + +if [ ! -d "$SHELL_DIR" ]; then + echo "Shell directory not found: $SHELL_DIR" >&2 + exit 1 +fi + +# Create necessary directories +mkdir -p ~/.config/gtk-3.0 ~/.config/gtk-4.0 ~/.config/qt5ct/colors ~/.config/qt6ct/colors ~/.local/share/color-schemes + +# Change to shell directory where matugen-config.toml is located +cd "$SHELL_DIR" || exit 1 + +# Verify config file exists +if [ ! -f "matugen-config.toml" ]; then + echo "Config file not found: $SHELL_DIR/matugen-config.toml" >&2 + exit 1 +fi + +# Generate themes using matugen with verbose output +echo "Generating system themes from wallpaper: $WALLPAPER_PATH" +echo "Using config: $SHELL_DIR/matugen-config.toml" + +if ! matugen -v -c matugen-config.toml image "$WALLPAPER_PATH"; then + echo "Failed to generate system themes with matugen" >&2 + exit 1 +fi + +echo "System theme files generated successfully" \ No newline at end of file diff --git a/matugen-config.toml b/matugen-config.toml new file mode 100644 index 00000000..6d6e5ab8 --- /dev/null +++ b/matugen-config.toml @@ -0,0 +1,13 @@ +[config] + +[templates.gtk3] +input_path = './templates/gtk-colors.css' +output_path = '~/.config/gtk-3.0/colors.css' + +[templates.gtk4] +input_path = './templates/gtk-colors.css' +output_path = '~/.config/gtk-4.0/colors.css' + +[templates.qt] +input_path = './templates/qt-colors.colors' +output_path = '~/.local/share/color-schemes/Matugen.colors' \ No newline at end of file diff --git a/templates/gtk-colors.css b/templates/gtk-colors.css new file mode 100644 index 00000000..8206ede7 --- /dev/null +++ b/templates/gtk-colors.css @@ -0,0 +1,22 @@ +/* +* GTK Colors +* Generated with Matugen +*/ + +@define-color accent_color {{colors.primary_fixed_dim.default.hex}}; +@define-color accent_fg_color {{colors.on_primary_fixed.default.hex}}; +@define-color accent_bg_color {{colors.primary_fixed_dim.default.hex}}; +@define-color window_bg_color {{colors.surface_dim.default.hex}}; +@define-color window_fg_color {{colors.on_surface.default.hex}}; +@define-color headerbar_bg_color {{colors.surface_dim.default.hex}}; +@define-color headerbar_fg_color {{colors.on_surface.default.hex}}; +@define-color popover_bg_color {{colors.surface_dim.default.hex}}; +@define-color popover_fg_color {{colors.on_surface.default.hex}}; +@define-color view_bg_color {{colors.surface.default.hex}}; +@define-color view_fg_color {{colors.on_surface.default.hex}}; +@define-color card_bg_color {{colors.surface.default.hex}}; +@define-color card_fg_color {{colors.on_surface.default.hex}}; +@define-color sidebar_bg_color @window_bg_color; +@define-color sidebar_fg_color @window_fg_color; +@define-color sidebar_border_color @window_bg_color; +@define-color sidebar_backdrop_color @window_bg_color; \ No newline at end of file diff --git a/templates/qt-colors.colors b/templates/qt-colors.colors new file mode 100644 index 00000000..737d1bf2 --- /dev/null +++ b/templates/qt-colors.colors @@ -0,0 +1,82 @@ +[ColorEffects:Disabled] +Color={{colors.surface_dim.default.hex}} +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color={{colors.surface_variant.default.hex}} +ColorAmount=0.025 +ColorEffect=2 +ContrastAmount=0.1 +ContrastEffect=2 +Enable=false +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Button] +BackgroundAlternate={{colors.surface_container_low.default.hex}} +BackgroundNormal={{colors.surface_container_high.default.hex}} +DecorationFocus={{colors.primary.default.hex}} +DecorationHover={{colors.primary.default.hex}} +ForegroundActive={{colors.primary.default.hex}} +ForegroundInactive={{colors.on_surface_variant.default.hex}} +ForegroundLink={{colors.secondary.default.hex}} +ForegroundNegative={{colors.error.default.hex}} +ForegroundNeutral={{colors.tertiary.default.hex}} +ForegroundNormal={{colors.on_surface.default.hex}} +ForegroundPositive={{colors.tertiary_fixed.default.hex}} +ForegroundVisited={{colors.on_secondary_container.default.hex}} + +[Colors:Selection] +BackgroundAlternate={{colors.surface_container_low.default.hex}} +BackgroundNormal={{colors.primary.default.hex}} +DecorationFocus={{colors.primary.default.hex}} +DecorationHover={{colors.primary.default.hex}} +ForegroundActive={{colors.on_primary.default.hex}} +ForegroundInactive={{colors.on_surface_variant.default.hex}} +ForegroundLink={{colors.secondary_fixed.default.hex}} +ForegroundNegative={{colors.error_container.default.hex}} +ForegroundNeutral={{colors.tertiary_fixed_dim.default.hex}} +ForegroundNormal={{colors.on_primary.default.hex}} +ForegroundPositive={{colors.tertiary_container.default.hex}} +ForegroundVisited={{colors.on_secondary_container.default.hex}} + +[Colors:View] +BackgroundAlternate={{colors.surface_container.default.hex}} +BackgroundNormal={{colors.background.default.hex}} +DecorationFocus={{colors.on_primary_container.default.hex}} +DecorationHover={{colors.on_primary.default.hex}} +ForegroundActive={{colors.primary.default.hex}} +ForegroundInactive={{colors.on_surface_variant.default.hex}} +ForegroundLink={{colors.secondary.default.hex}} +ForegroundNegative={{colors.error.default.hex}} +ForegroundNeutral={{colors.tertiary.default.hex}} +ForegroundNormal={{colors.on_background.default.hex}} +ForegroundPositive={{colors.tertiary_fixed.default.hex}} +ForegroundVisited={{colors.on_secondary_container.default.hex}} + +[Colors:Window] +BackgroundAlternate={{colors.primary_container.default.hex}} +BackgroundNormal={{colors.surface_container.default.hex}} +DecorationFocus={{colors.primary.default.hex}} +DecorationHover={{colors.primary.default.hex}} +ForegroundActive={{colors.primary.default.hex}} +ForegroundInactive={{colors.on_surface_variant.default.hex}} +ForegroundLink={{colors.secondary.default.hex}} +ForegroundNegative={{colors.error.default.hex}} +ForegroundNeutral={{colors.tertiary.default.hex}} +ForegroundNormal={{colors.on_background.default.hex}} +ForegroundPositive={{colors.tertiary_fixed.default.hex}} +ForegroundVisited={{colors.on_secondary_container.default.hex}} + +[General] +ColorScheme=Matugen +Name=Matugen + +[KDE] +contrast=4 \ No newline at end of file