"""The app look: neutral dark greys, one dark red accent, the wallpaper behind the filter panel. `apply(app)` sets the style for the whole application; `Sidebar` is the filter panel, painted over the wallpaper (app/assets/wallpaper.jpg) when the background is on. """ from PySide6.QtCore import Qt from PySide6.QtGui import QColor, QImage, QPainter, QPalette from PySide6.QtWidgets import QWidget from . import icons ACCENT = "#b0242c" ACCENT_HI = "#cf3a42" STEEL = "#c4c4c4" # the original waveform: neutral light grey TEXT = "#e6e6e6" MUTED = "#9a9a9a" BG = "#141414" # dialogs PANEL_BG = "#0c0c0c" # main window: behind the filter and editor panels (darker for contrast) SURFACE = "#1b1b1b" BORDER = "#2c2c2c" CONTROL = "#3a3a3a" QSS = """ QWidget { color: %(TEXT)s; } QMainWindow { background: %(PANEL_BG)s; } QDialog { background: %(BG)s; } QToolTip { background: #202020; color: %(TEXT)s; border: 1px solid %(CONTROL)s; padding: 4px; } QToolBar { background: #0f0f0f; border: none; border-bottom: 1px solid %(BORDER)s; spacing: 6px; padding: 4px 6px; } QToolBar QToolButton { color: %(TEXT)s; padding: 4px 9px; border-radius: 4px; } QToolBar QToolButton:hover { background: #262626; } QLabel#AppTitle { color: %(TEXT)s; font-weight: bold; padding: 0 10px 0 2px; } QStatusBar { background: #0f0f0f; color: %(MUTED)s; border-top: 1px solid %(BORDER)s; } QStatusBar QLabel { color: %(MUTED)s; } QWidget#Sidebar { background: transparent; border-right: 1px solid %(BORDER)s; } QWidget#EditorPanel, QScrollArea, QScrollArea > QWidget > QWidget { background: transparent; } QWidget#Banner { background: #3a1416; border-bottom: 1px solid %(ACCENT)s; } QLabel#EditorTitle { color: %(TEXT)s; } QLabel#Muted { color: %(MUTED)s; } QTableView { background: %(SURFACE)s; alternate-background-color: #202020; gridline-color: %(BORDER)s; border: 1px solid %(BORDER)s; selection-background-color: rgba(176,36,44,70); selection-color: #ffffff; } QTableView::item { padding: 0 4px; } QHeaderView::section { background: #171717; color: %(MUTED)s; border: none; border-right: 1px solid %(BORDER)s; border-bottom: 1px solid %(CONTROL)s; padding: 4px 6px; font-weight: bold; } QTableCornerButton::section { background: #171717; border: none; } QGroupBox { background: %(SURFACE)s; border: 1px solid %(BORDER)s; border-radius: 6px; margin-top: 16px; padding: 10px 6px 6px 6px; } QGroupBox::title { subcontrol-origin: margin; left: 12px; padding: 0 6px; color: %(MUTED)s; font-weight: bold; } QPushButton { background: #242424; border: 1px solid %(CONTROL)s; border-radius: 4px; padding: 5px 12px; } QPushButton:hover { background: #2c2c2c; border-color: #4a4a4a; } QPushButton:pressed, QPushButton:checked { background: rgba(176,36,44,45); border-color: %(ACCENT)s; } QPushButton:disabled { color: #5c5c5c; border-color: #2a2a2a; background: #1c1c1c; } QPushButton#Primary { background: %(ACCENT)s; color: #ffffff; font-weight: bold; border: 1px solid %(ACCENT)s; } QPushButton#Primary:hover { background: %(ACCENT_HI)s; border-color: %(ACCENT_HI)s; } QPushButton#Primary:disabled { background: #3a1c1e; color: #8a6466; border-color: #3a1c1e; } QLineEdit, QComboBox, QDoubleSpinBox, QPlainTextEdit, QListWidget, QTreeWidget { background: #101010; border: 1px solid %(CONTROL)s; border-radius: 4px; padding: 3px 6px; selection-background-color: rgba(176,36,44,120); } QLineEdit:focus, QComboBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus { border-color: %(ACCENT)s; } QComboBox QAbstractItemView { background: #1b1b1b; border: 1px solid %(CONTROL)s; selection-background-color: rgba(176,36,44,90); } QCheckBox::indicator, QGroupBox::indicator, QListView::indicator { width: 14px; height: 14px; border: 1px solid #555555; border-radius: 3px; background: #101010; } QCheckBox::indicator:checked, QGroupBox::indicator:checked, QListView::indicator:checked { background: %(ACCENT)s; border-color: %(ACCENT)s; } QProgressBar { background: #101010; border: 1px solid %(CONTROL)s; border-radius: 3px; height: 10px; } QProgressBar::chunk { background: %(ACCENT)s; border-radius: 2px; } QSlider::groove:horizontal { height: 4px; background: #333333; border-radius: 2px; } QSlider::sub-page:horizontal { background: %(ACCENT)s; border-radius: 2px; } QSlider::handle:horizontal { background: %(TEXT)s; width: 12px; margin: -5px 0; border-radius: 6px; } QSplitter::handle { background: %(BORDER)s; } QScrollBar:vertical, QScrollBar:horizontal { background: transparent; border: none; margin: 0; } QScrollBar:vertical { width: 10px; } QScrollBar:horizontal { height: 10px; } QScrollBar::handle { background: #3c3c3c; border-radius: 4px; min-height: 24px; min-width: 24px; } QScrollBar::handle:hover { background: #555555; } QScrollBar::add-line, QScrollBar::sub-line { width: 0; height: 0; } QMenu { background: #1b1b1b; border: 1px solid %(CONTROL)s; } QMenu::item { padding: 5px 22px 5px 10px; } QMenu::item:selected { background: rgba(176,36,44,70); } """ % dict(TEXT=TEXT, BG=BG, PANEL_BG=PANEL_BG, ACCENT=ACCENT, ACCENT_HI=ACCENT_HI, MUTED=MUTED, BORDER=BORDER, SURFACE=SURFACE, CONTROL=CONTROL) # waveform colours: original / installed / replacement WAVE = {"orig": QColor(STEEL), "inst": QColor("#8f4a4e"), "new": QColor(ACCENT)} WAVE_BG = QColor("#111111") SIDEBAR_VEIL = QColor(16, 16, 16) # alpha follows the background strength def apply(app): app.setStyle("Fusion") pal = QPalette() for role, col in [(QPalette.ColorRole.Window, BG), (QPalette.ColorRole.WindowText, TEXT), (QPalette.ColorRole.Base, "#101010"), (QPalette.ColorRole.AlternateBase, "#202020"), (QPalette.ColorRole.Text, TEXT), (QPalette.ColorRole.Button, "#242424"), (QPalette.ColorRole.ButtonText, TEXT), (QPalette.ColorRole.Highlight, ACCENT), (QPalette.ColorRole.HighlightedText, "#ffffff"), (QPalette.ColorRole.ToolTipBase, "#202020"), (QPalette.ColorRole.ToolTipText, TEXT), (QPalette.ColorRole.PlaceholderText, MUTED), (QPalette.ColorRole.Mid, "#333333"), (QPalette.ColorRole.Link, ACCENT)]: pal.setColor(role, QColor(col)) pal.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor("#5c5c5c")) pal.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor("#5c5c5c")) app.setPalette(pal) app.setStyleSheet(QSS) def wallpaper(): """The Arkham City wallpaper (app/assets/wallpaper.jpg) as a QImage, or None if the assets are missing.""" p = icons.asset("wallpaper.jpg") img = QImage(p) if p else QImage() return None if img.isNull() else img class Sidebar(QWidget): """Filter panel with the wallpaper behind it: scaled to the panel height, its left part fills the panel.""" def __init__(self, parent=None): super().__init__(parent) self.setObjectName("Sidebar") self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) self.image = None self.opacity = 0.1 self._scaled = None def configure(self, settings): st = settings or {} self.image = wallpaper() if st.get("background_enabled", True) else None self.opacity = max(0.0, min(1.0, float(st.get("background_opacity", 0.1)))) self._scaled = None self.update() def paintEvent(self, e): if self.image is not None: if self._scaled is None or self._scaled.height() != self.height(): self._scaled = self.image.scaledToHeight(self.height(), Qt.TransformationMode.SmoothTransformation) p = QPainter(self) p.setOpacity(self.opacity) p.drawImage(0, 0, self._scaled) p.setOpacity(1.0) veil = QColor(SIDEBAR_VEIL) veil.setAlpha(int(180 * self.opacity)) # stronger image, stronger veil: labels stay readable p.fillRect(self.rect(), veil) p.end() super().paintEvent(e)