Dark theme, scan auto-ingest, packaging prep, and README refresh
This commit is contained in:
+313
@@ -0,0 +1,313 @@
|
||||
"""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
|
||||
|
||||
# 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: none;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 5px solid {TEXT_DIM};
|
||||
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())
|
||||
app.setStyleSheet(_STYLESHEET)
|
||||
Reference in New Issue
Block a user