"""Global dark theme for the GUI (Fusion style + palette + stylesheet). Applied once at startup from ``gui.main``. The per-widget ``setStyleSheet`` colors sprinkled through ``gui.py`` (``#999`` secondary text, ``#e06c6c`` delete buttons, ``#14151a`` preview panels) were written for a dark background, so a single cohesive dark theme here makes the whole UI consistent. """ from PySide6.QtGui import QColor, QPalette from . import config # Design tokens BG = "#1e1f24" # window background SURFACE = "#26272e" # cards, inputs, table headers RAISED = "#2d2e36" # buttons, combos, menus BORDER = "#3a3b44" BORDER_SOFT = "#33343d" TEXT = "#e6e6ea" TEXT_DIM = "#9a9ba5" TEXT_FAINT = "#6f7079" ACCENT = "#4a9eff" ACCENT_HOVER = "#6bb0ff" _STYLESHEET = f""" QMainWindow {{ background: {BG}; }} QWidget {{ color: {TEXT}; }} /* --- Tabs --- */ QTabWidget::pane {{ border: 1px solid {BORDER}; border-radius: 6px; top: -1px; }} QTabBar::tab {{ background: transparent; color: {TEXT_DIM}; padding: 8px 18px; border: none; border-bottom: 2px solid transparent; margin-right: 2px; }} QTabBar::tab:hover:!selected {{ color: {TEXT}; }} QTabBar::tab:selected {{ color: {TEXT}; border-bottom: 2px solid {ACCENT}; }} /* --- Buttons --- */ QPushButton {{ background: {RAISED}; border: 1px solid {BORDER}; border-radius: 6px; padding: 6px 12px; }} QPushButton:hover {{ background: #343640; border-color: {ACCENT_HOVER}; }} QPushButton:pressed {{ background: {SURFACE}; }} QPushButton:disabled {{ color: {TEXT_FAINT}; background: {SURFACE}; border-color: {BORDER_SOFT}; }} QPushButton:checked {{ background: {ACCENT}; border-color: {ACCENT}; color: #101116; }} /* --- Inputs --- */ QLineEdit, QSpinBox, QPlainTextEdit, QTextEdit {{ background: {SURFACE}; border: 1px solid {BORDER}; border-radius: 6px; padding: 5px 8px; selection-background-color: {ACCENT}; selection-color: #101116; }} QLineEdit:focus, QSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus {{ border-color: {ACCENT}; }} QLineEdit:disabled, QSpinBox:disabled {{ color: {TEXT_FAINT}; }} /* --- Combo boxes --- */ QComboBox {{ background: {RAISED}; border: 1px solid {BORDER}; border-radius: 6px; padding: 4px 10px; }} QComboBox:hover {{ border-color: {ACCENT_HOVER}; }} QComboBox::drop-down {{ border: none; width: 22px; }} QComboBox::down-arrow {{ image: url(__CHEVRON__); width: 12px; height: 8px; margin-right: 6px; }} QComboBox QAbstractItemView {{ background: {RAISED}; border: 1px solid {BORDER}; border-radius: 6px; selection-background-color: rgba(74, 158, 255, 0.25); selection-color: {TEXT}; outline: 0; }} /* --- Tables --- */ QTableWidget {{ background: transparent; border: none; gridline-color: {BORDER_SOFT}; }} QHeaderView::section {{ background: {SURFACE}; color: {TEXT_DIM}; border: none; border-bottom: 1px solid {BORDER}; padding: 6px 8px; }} QTableWidget::item {{ padding: 4px 6px; }} QTableWidget::item:selected {{ background: rgba(74, 158, 255, 0.22); color: {TEXT}; }} QTableCornerButton::section {{ background: {SURFACE}; border: none; border-bottom: 1px solid {BORDER}; }} /* --- Cover grid / lists --- */ QListWidget {{ background: transparent; border: none; }} QListWidget::item {{ border-radius: 6px; }} QListWidget::item:hover:!selected {{ background: rgba(255, 255, 255, 0.04); }} QListWidget::item:selected {{ background: rgba(74, 158, 255, 0.22); }} /* --- Cards (details panels use QScrollArea) --- */ QScrollArea {{ background: {SURFACE}; border: none; }} /* --- Menus --- */ QMenu {{ background: {RAISED}; border: 1px solid {BORDER}; border-radius: 6px; padding: 4px; }} QMenu::item {{ padding: 6px 26px 6px 12px; border-radius: 4px; }} QMenu::item:selected {{ background: rgba(74, 158, 255, 0.25); }} QMenu::separator {{ height: 1px; background: {BORDER}; margin: 4px 8px; }} /* --- Scrollbars --- */ QScrollBar:vertical {{ background: transparent; width: 10px; margin: 0; }} QScrollBar::handle:vertical {{ background: {BORDER}; border-radius: 5px; min-height: 30px; }} QScrollBar::handle:vertical:hover {{ background: {ACCENT}; }} QScrollBar:horizontal {{ background: transparent; height: 10px; margin: 0; }} QScrollBar::handle:horizontal {{ background: {BORDER}; border-radius: 5px; min-width: 30px; }} QScrollBar::handle:horizontal:hover {{ background: {ACCENT}; }} QScrollBar::add-line, QScrollBar::sub-line {{ width: 0; height: 0; }} QScrollBar::add-page, QScrollBar::sub-page {{ background: transparent; }} /* --- Splitter --- */ QSplitter::handle {{ background: transparent; }} QSplitter::handle:horizontal {{ width: 1px; }} QSplitter::handle:vertical {{ height: 1px; }} /* --- Misc --- */ QToolTip {{ background: {RAISED}; color: {TEXT}; border: 1px solid {BORDER}; padding: 4px 6px; }} QStatusBar {{ background: {SURFACE}; color: {TEXT_DIM}; border-top: 1px solid {BORDER}; }} QProgressBar {{ background: {SURFACE}; border: 1px solid {BORDER}; border-radius: 6px; text-align: center; }} QProgressBar::chunk {{ background: {ACCENT}; border-radius: 5px; }} QCheckBox {{ spacing: 8px; }} QCheckBox::indicator {{ width: 16px; height: 16px; border: 1px solid {BORDER}; border-radius: 4px; background: {SURFACE}; }} QCheckBox::indicator:checked {{ background: {ACCENT}; border-color: {ACCENT}; }} QGroupBox {{ border: 1px solid {BORDER}; border-radius: 6px; margin-top: 10px; }} QGroupBox::title {{ subcontrol-origin: margin; left: 10px; padding: 0 4px; color: {TEXT_DIM}; }} """ def _dark_palette(): p = QPalette() p.setColor(QPalette.Window, QColor(BG)) p.setColor(QPalette.WindowText, QColor(TEXT)) p.setColor(QPalette.Base, QColor(SURFACE)) p.setColor(QPalette.AlternateBase, QColor(RAISED)) p.setColor(QPalette.ToolTipBase, QColor(RAISED)) p.setColor(QPalette.ToolTipText, QColor(TEXT)) p.setColor(QPalette.Text, QColor(TEXT)) p.setColor(QPalette.Button, QColor(RAISED)) p.setColor(QPalette.ButtonText, QColor(TEXT)) p.setColor(QPalette.BrightText, QColor("#ff5c5c")) p.setColor(QPalette.Link, QColor(ACCENT)) p.setColor(QPalette.Highlight, QColor(ACCENT)) p.setColor(QPalette.HighlightedText, QColor("#101116")) p.setColor(QPalette.PlaceholderText, QColor(TEXT_FAINT)) faint = QColor(TEXT_FAINT) for role in (QPalette.WindowText, QPalette.Text, QPalette.ButtonText): p.setColor(QPalette.Disabled, role, faint) return p def apply_theme(app): """Apply the dark Fusion theme to ``app`` (call once before showing UI).""" app.setStyle("Fusion") app.setPalette(_dark_palette()) chevron = str(config.resource_path("img", "chevron.png")) app.setStyleSheet(_STYLESHEET.replace("__CHEVRON__", chevron))