142 lines
7.9 KiB
Python
142 lines
7.9 KiB
Python
"""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)
|