1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 22:15:38 -05:00

feat: Integrate full shell dynamic theming system

This commit is contained in:
purian23
2025-07-28 18:29:16 -04:00
parent 99e890cc17
commit 102b248cad
9 changed files with 420 additions and 6 deletions

View File

@@ -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 - **Quickshell Framework** - QML-based framework for building desktop shells
- **Qt/QtQuick** - UI rendering and controls - **Qt/QtQuick** - UI rendering and controls
- **Wayland** - Display server protocol - **Wayland** - Display server protocol
- **Matugen** - Dynamic theming system for wallpaper-based colors and system app theming
## Development Commands ## Development Commands
@@ -162,6 +163,7 @@ shell.qml # Main entry point (minimal orchestration)
- **AppLauncher**: Full-featured app grid/list with 93+ applications, search, categories - **AppLauncher**: Full-featured app grid/list with 93+ applications, search, categories
- **ClipboardHistoryModal**: Complete clipboard management with cliphist integration - **ClipboardHistoryModal**: Complete clipboard management with cliphist integration
- **TopBar**: Per-monitor panels with workspace switching, clock, system tray - **TopBar**: Per-monitor panels with workspace switching, clock, system tray
- **System App Theming**: Automatic GTK and Qt application theming using matugen templates
#### Key Widgets #### Key Widgets

View File

@@ -14,8 +14,12 @@ Singleton {
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation) readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
readonly property string homeDir: _homeUrl.startsWith("file://") ? _homeUrl.substring(7) : _homeUrl 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 readonly property string wallpaperPath: Prefs.wallpaperPath
property bool matugenAvailable: false property bool matugenAvailable: false
property bool gtkThemingEnabled: false
property bool qtThemingEnabled: false
property bool systemThemeGenerationInProgress: false
property string matugenJson: "" property string matugenJson: ""
property var matugenColors: ({ property var matugenColors: ({
}) })
@@ -81,6 +85,8 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
console.log("Colors.qml → home =", homeDir); console.log("Colors.qml → home =", homeDir);
matugenCheck.running = true; matugenCheck.running = true;
checkGtkThemingAvailability();
checkQtThemingAvailability();
if (typeof Theme !== "undefined") if (typeof Theme !== "undefined")
Theme.isLightModeChanged.connect(root.onLightModeChanged); Theme.isLightModeChanged.connect(root.onLightModeChanged);
} }
@@ -165,6 +171,13 @@ Singleton {
generateNiriConfig(); generateNiriConfig();
generateGhosttyConfig(); generateGhosttyConfig();
if (gtkThemingEnabled && typeof Prefs !== "undefined" && Prefs.gtkThemingEnabled) {
generateGtkThemes();
}
if (qtThemingEnabled && typeof Prefs !== "undefined" && Prefs.qtThemingEnabled) {
generateQtThemes();
}
} }
function generateNiriConfig() { function generateNiriConfig() {
@@ -242,6 +255,49 @@ palette = 15=${fg_b}`;
ghosttyConfigWriter.running = true; 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 { Process {
id: niriConfigWriter id: niriConfigWriter
running: false running: false
@@ -266,4 +322,103 @@ 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
`];
}
}
} }

View File

@@ -52,6 +52,8 @@ Singleton {
property string fontFamily: "Inter Variable" property string fontFamily: "Inter Variable"
property string monoFontFamily: "Fira Code" property string monoFontFamily: "Fira Code"
property int fontWeight: Font.Normal property int fontWeight: Font.Normal
property bool gtkThemingEnabled: false
property bool qtThemingEnabled: false
readonly property string defaultFontFamily: "Inter Variable" readonly property string defaultFontFamily: "Inter Variable"
readonly property string defaultMonoFontFamily: "Fira Code" readonly property string defaultMonoFontFamily: "Fira Code"
@@ -131,6 +133,8 @@ Singleton {
fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : defaultFontFamily; fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : defaultFontFamily;
monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : defaultMonoFontFamily; monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : defaultMonoFontFamily;
fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal; fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal;
gtkThemingEnabled = settings.gtkThemingEnabled !== undefined ? settings.gtkThemingEnabled : false;
qtThemingEnabled = settings.qtThemingEnabled !== undefined ? settings.qtThemingEnabled : false;
applyStoredTheme(); applyStoredTheme();
detectAvailableIconThemes(); detectAvailableIconThemes();
detectQtTools(); detectQtTools();
@@ -182,7 +186,9 @@ Singleton {
"doNotDisturb": doNotDisturb, "doNotDisturb": doNotDisturb,
"fontFamily": fontFamily, "fontFamily": fontFamily,
"monoFontFamily": monoFontFamily, "monoFontFamily": monoFontFamily,
"fontWeight": fontWeight "fontWeight": fontWeight,
"gtkThemingEnabled": gtkThemingEnabled,
"qtThemingEnabled": qtThemingEnabled
}, null, 2)); }, null, 2));
} }
@@ -579,6 +585,16 @@ Singleton {
saveSettings(); saveSettings();
} }
function setGtkThemingEnabled(enabled) {
gtkThemingEnabled = enabled;
saveSettings();
}
function setQtThemingEnabled(enabled) {
qtThemingEnabled = enabled;
saveSettings();
}
// Helper to safely single-quote shell strings // Helper to safely single-quote shell strings
function _shq(s) { function _shq(s) {
return "'" + String(s).replace(/'/g, "'\\''") + "'"; return "'" + String(s).replace(/'/g, "'\\''") + "'";

View File

@@ -759,11 +759,82 @@ ScrollView {
} }
} }
} }
} }
}
// 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();
}
}
}
}
} }

View File

@@ -37,18 +37,26 @@ paru -S quickshell-git
|------------|---------|------------| |------------|---------|------------|
| cava | Equalizer in TopBar uses Audio Data | Equalizer shifts at random | | cava | Equalizer in TopBar uses Audio Data | Equalizer shifts at random |
| cliphist | Allows clipboard history view | No clipboard history view available | | 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 | | ddcutil (or brightnessctl) | Allows controlling brightness of monitors | No Brightness |
| wl-clipboard | Unlocks copy functionality of certain elements, such as process PIDs | No copy | | 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 ```bash
# Arch # 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 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 3. Install DankMaterialShell
``` ```

45
generate-themes.sh Executable file
View File

@@ -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 <wallpaper_path> <shell_dir>" >&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"

13
matugen-config.toml Normal file
View File

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

22
templates/gtk-colors.css Normal file
View File

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

View File

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