diff --git a/DocuMentor.wxs b/DocuMentor.wxs index e7d754d..1d8b320 100644 --- a/DocuMentor.wxs +++ b/DocuMentor.wxs @@ -4,7 +4,7 @@ QIcon: f.close() renderer = QSvgRenderer(svg_data) - result = QIcon() - for size in (16, 24, 32): - pixmap = QPixmap(size, size) - pixmap.fill(Qt.GlobalColor.transparent) - painter = QPainter(pixmap) - renderer.render(painter) - painter.end() - result.addPixmap(pixmap) + pixmap = QPixmap(_RENDER_SIZE, _RENDER_SIZE) + pixmap.fill(Qt.GlobalColor.transparent) + painter = QPainter(pixmap) + renderer.render(painter) + painter.end() + result = QIcon(pixmap) _ICON_CACHE[cache_key] = result return result + + +class IconRefreshMixin: + """ + Mischklasse für Top-Level-Widgets, deren Icons sich beim Theme-Wechsel automatisch neu einfärben. + + Voraussetzungen für die nutzende Klasse: + - Sie stellt eine Methode ``_setup_icons()`` bereit, die alle ihre Icons neu setzt. + - Nach abgeschlossener Initialisierung setzt sie ``self._icons_ready = True`` + (idealerweise zusammen mit ``self._last_icon_color``, siehe ``mark_icons_ready()``). + + Bei einem Style-/Palette-Wechsel wird – nur wenn sich die Theme-Textfarbe tatsächlich + geändert hat – ``_on_icon_theme_changed()`` aufgerufen (Standard: ``_setup_icons()``). + Unterklassen können ``_on_icon_theme_changed()`` überschreiben, um zusätzlich z.B. + Baum-Icons neu zu zeichnen. + + Hinweis: ``IconRefreshMixin`` muss in der Klassenbasis VOR der Qt-Basisklasse + (``QMainWindow``/``QDialog``) stehen, damit das überschriebene ``changeEvent`` greift. + """ + + _icons_ready: bool = False + _last_icon_color: str | None = None + + @staticmethod + def _current_text_color() -> str | None: + app = QApplication.instance() + if app is None: + return None + return app.palette().color(QPalette.ColorRole.WindowText).name() + + def mark_icons_ready(self): + """Schaltet die Theme-Reaktivität scharf (nach dem ersten ``_setup_icons()`` aufrufen).""" + self._last_icon_color = self._current_text_color() + self._icons_ready = True + + def changeEvent(self, event): + if event.type() in (QEvent.Type.PaletteChange, QEvent.Type.StyleChange) and self._icons_ready: + color = self._current_text_color() + if color is not None and color != self._last_icon_color: + self._last_icon_color = color + self._on_icon_theme_changed() + super().changeEvent(event) + + def _on_icon_theme_changed(self): + """Reaktion auf eine geänderte Theme-Textfarbe. Standard: alle Icons neu setzen.""" + self._setup_icons() diff --git a/src/ui/MainWindow.py b/src/ui/MainWindow.py index 0d389f2..8521825 100644 --- a/src/ui/MainWindow.py +++ b/src/ui/MainWindow.py @@ -25,7 +25,7 @@ from ui.mixins import ( TransformationMixin, ) from conf import app_settings, Project, ProjectData, TreeNode, XslFile -from icons import icon +from icons import icon, IconRefreshMixin from pathlib import Path @@ -33,6 +33,7 @@ logger = logging.getLogger(__name__) class MainWindow( + IconRefreshMixin, QMainWindow, TreeManagerMixin, PdfViewerMixin, @@ -141,8 +142,11 @@ class MainWindow( # Zoom per Mausrad (STRG+Mausrad) für PDF-Viewer aktivieren self._setup_scroll_area_zoom() - # Icons setzen (überschreibt fromTheme()-Icons aus _ui.py) + # Icons setzen (überschreibt fromTheme()-Icons aus _ui.py). Läuft nach + # _connect_signals(), daher existieren auch die programmatisch erzeugten Aktionen. self._setup_icons() + # Ab hier reagiert das Fenster auf Theme-Wechsel (siehe IconRefreshMixin) + self.mark_icons_ready() # Gespeicherte UI-Zustände wiederherstellen self._restore_ui_state() @@ -157,10 +161,14 @@ class MainWindow( self.ui.view_ref_pdf.setIcon(icon("file")) self.ui.view_new_pdf.setIcon(icon("file")) self.ui.accept_changes.setIcon(icon("check-circle")) - if hasattr(self, "action_xsl_dependencies"): - self.action_xsl_dependencies.setIcon(icon("git-branch")) - if hasattr(self, "action_info"): - self.action_info.setIcon(icon("info")) + self.action_xsl_dependencies.setIcon(icon("git-branch")) + self.action_info.setIcon(icon("info")) + + def _on_icon_theme_changed(self): + """Theme-Wechsel: Icons neu einfärben und – falls ein Projekt offen ist – die Baum-Icons.""" + self._setup_icons() + if getattr(self, "pdf_project", None): + self._load_nodes_to_tree() def _restore_ui_state(self): """Stellt die gespeicherten UI-Zustände wieder her (Fenstergeometrie, Splitter, TreeWidget-Spalten).""" @@ -358,9 +366,8 @@ class MainWindow( logger.info(f"Theme erfolgreich gewechselt zu: {theme_name}") app_settings.theme = theme_name app_settings.save() - self._setup_icons() - if hasattr(self, "pdf_project") and self.pdf_project: - self._load_nodes_to_tree() + # Icons/Baum werden über changeEvent (PaletteChange/StyleChange) neu eingefärbt, + # siehe IconRefreshMixin._on_icon_theme_changed() else: logger.error(f"Fehler: Theme '{theme_name}' konnte nicht erstellt werden") diff --git a/src/ui/XslDependencyDialog.py b/src/ui/XslDependencyDialog.py index 442233b..0d33414 100644 --- a/src/ui/XslDependencyDialog.py +++ b/src/ui/XslDependencyDialog.py @@ -12,7 +12,7 @@ import tempfile from pathlib import Path from PySide6.QtCore import QUrl, Qt -from icons import icon +from icons import icon, IconRefreshMixin from PySide6.QtWidgets import ( QComboBox, QDialog, @@ -120,7 +120,7 @@ except ImportError: logger.warning("PySide6-WebEngine nicht verfügbar — Netzwerkgraph-Tab deaktiviert") -class XslDependencyDialog(QDialog): +class XslDependencyDialog(IconRefreshMixin, QDialog): """Dialog zur Anzeige des vollständigen XSL-Abhängigkeitsgraphen.""" def __init__( @@ -185,6 +185,25 @@ class XslDependencyDialog(QDialog): self._build_layout_controls() self._restore_graph_layout_settings() + # Ab hier auf Theme-Wechsel reagieren (siehe IconRefreshMixin) + self.mark_icons_ready() + + def _setup_icons(self): + """Färbt alle Icons gemäß aktuellem Theme neu ein (Button und Baum-Icons).""" + self.ui.settingsButton.setIcon(icon("sliders")) + + # Datei-Baum neu einfärben, dabei die aktuelle Auswahl erhalten + current = self.ui.fileTree.currentItem() + current_path = current.data(0, Qt.ItemDataRole.UserRole) if current else None + self._populate_file_tree() + if current_path is not None: + for i in range(self.ui.fileTree.topLevelItemCount()): + item = self.ui.fileTree.topLevelItem(i) + if item.data(0, Qt.ItemDataRole.UserRole) == current_path: + # Setzt die Auswahl → _on_file_selected zeichnet den Abhängigkeitsbaum neu ein + self.ui.fileTree.setCurrentItem(item) + break + def reject(self): """Speichert Layout-Einstellungen und räumt temporäre Dateien und QWebEngineView auf.""" self._save_graph_layout_settings() diff --git a/uv.lock b/uv.lock index b6a2a85..98adab2 100644 --- a/uv.lock +++ b/uv.lock @@ -39,7 +39,7 @@ wheels = [ [[package]] name = "documentor" -version = "1.7.1" +version = "1.7.2" source = { virtual = "." } dependencies = [ { name = "connectorx" },