Skip to content

Commit 48fe0f9

Browse files
committed
fix(theming): skip theme writes and hooks when wallpaper change produces no diff
Issue: #2400
1 parent b8de6e1 commit 48fe0f9

3 files changed

Lines changed: 33 additions & 8 deletions

File tree

Scripts/python/src/theming/lib/renderer.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import sys
2323
from dataclasses import dataclass, field
2424
from pathlib import Path
25-
from typing import Any, Optional, Union
25+
from typing import Any, Optional, Tuple, Union
2626

2727
try:
2828
import tomllib
@@ -970,22 +970,34 @@ def render(self, template_text: str) -> str:
970970

971971
return result
972972

973-
def render_file(self, input_path: Path, output_path: Path) -> bool:
973+
def render_file(self, input_path: Path, output_path: Path) -> Tuple[bool, bool]:
974974
"""Render a template file to an output path.
975975
976-
Returns True if successful, False if skipped due to errors.
976+
Returns (success, wrote) where wrote is False if the file was skipped because
977+
content was already identical (avoids mtime bumps and app reloads).
977978
"""
978979
self._current_file = str(input_path)
979980
success = False
981+
wrote = False
980982
try:
981983
template_text = input_path.read_text()
982984
rendered_text = self.render(template_text)
983985

984986
if self._error_count > 0:
985987
print(f"Skipping {output_path}: template has {self._error_count} error(s)", file=sys.stderr)
986988
else:
987-
output_path.parent.mkdir(parents=True, exist_ok=True)
988-
output_path.write_text(rendered_text)
989+
out = Path(output_path).expanduser()
990+
out.parent.mkdir(parents=True, exist_ok=True)
991+
skip_write = False
992+
if out.is_file():
993+
try:
994+
if out.read_text() == rendered_text:
995+
skip_write = True
996+
except OSError:
997+
pass
998+
if not skip_write:
999+
out.write_text(rendered_text)
1000+
wrote = True
9891001
success = True
9901002
except FileNotFoundError:
9911003
self._log_error(f"Template file not found: {input_path}")
@@ -995,7 +1007,7 @@ def render_file(self, input_path: Path, output_path: Path) -> bool:
9951007
self._log_error(f"Unexpected error: {e}")
9961008
finally:
9971009
self._current_file = None
998-
return success
1010+
return success, wrote
9991011

10001012
# --- Custom Colors ---
10011013

@@ -1151,7 +1163,13 @@ def process_config_file(self, config_path: Path):
11511163
rendered_compare_to = self.render(compare_to)
11521164
self.closest_color = find_closest_color(rendered_compare_to, colors_to_compare)
11531165

1154-
self.render_file(Path(input_path).expanduser(), Path(output_path).expanduser())
1166+
ok, wrote = self.render_file(Path(input_path).expanduser(), Path(output_path).expanduser())
1167+
if not ok:
1168+
continue
1169+
1170+
# Hooks reload external apps (e.g. Discord); skip when output unchanged
1171+
if not wrote:
1172+
continue
11551173

11561174
# Execute pre_hook if specified
11571175
pre_hook = template.get("pre_hook")

Services/Theming/AppThemeService.qml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ Singleton {
2323

2424
if (Settings.data.colorSchemes.useWallpaperColors) {
2525
generateFromWallpaper();
26+
} else if (ColorSchemeService.lastPredefinedSchemeData) {
27+
// Regenerate templates only; skip applyScheme so colors.json and scheme reload stay untouched
28+
// when outputs are unchanged (see template processor skip-identical writes).
29+
generateFromPredefinedScheme(ColorSchemeService.lastPredefinedSchemeData);
2630
} else {
27-
// Re-run predefined scheme templates so {{image}} reflects the new wallpaper path
2831
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme);
2932
}
3033
}

Services/Theming/ColorSchemeService.qml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Singleton {
1616
property string schemesDirectory: Quickshell.shellDir + "/Assets/ColorScheme"
1717
property string downloadedSchemesDirectory: Settings.configDir + "colorschemes"
1818
property string colorsJsonFilePath: Settings.configDir + "colors.json"
19+
// Last successfully parsed predefined scheme JSON (full object). Used to refresh app templates
20+
// on wallpaper changes without re-running applyScheme (avoids rewriting colors.json when unchanged).
21+
property var lastPredefinedSchemeData: null
1922
readonly property string gtkRefreshScript: Quickshell.shellDir + "/Scripts/python/src/theming/gtk-refresh.py"
2023

2124
// prefer-light/prefer-dark only; GTK template post_hook still runs full gtk-refresh.
@@ -203,6 +206,7 @@ Singleton {
203206
}
204207
}
205208
writeColorsToDisk(variant);
209+
lastPredefinedSchemeData = data;
206210
Logger.i("ColorScheme", "Applying color scheme:", getBasename(path));
207211

208212
// Generate templates for predefined color schemes

0 commit comments

Comments
 (0)