feat: add 5 new themes via single QSS template engine
Replaces the two hard-coded .qss files with a single
resources/style_template.qss that uses @{token} placeholders.
apply_qss() now does regex substitution at runtime, so adding a
theme only requires a palette dict — no duplicate stylesheets.
New themes (Preferences → Color theme):
• One Dark Pro — VS Code classic dark
• Nord — arctic blue-gray dark
• Tokyo Night — deep purple-dark
• Dracula — classic purple-dark
• GitHub Light — clean minimal light
Existing Catppuccin Frappé (dark) and Latte (light) are unchanged.
Ctrl+Shift+T quick-toggle now recognises all dark themes via is_dark().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,589 @@
|
||||
/* ═══════════════════════════════════════════════════════════════════════════
|
||||
DBClient — Theme template (tokens replaced at runtime by theme.py)
|
||||
═══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── Global reset ───────────────────────────────────────────────────────── */
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
QMainWindow, QDialog {
|
||||
background-color: @{base};
|
||||
color: @{text};
|
||||
}
|
||||
|
||||
QWidget {
|
||||
background-color: @{base};
|
||||
color: @{text};
|
||||
font-family: "Segoe UI", "Inter", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
/* ── Menu bar ───────────────────────────────────────────────────────────── */
|
||||
QMenuBar {
|
||||
background-color: @{crust};
|
||||
color: @{text};
|
||||
border-bottom: 1px solid @{surface0};
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
QMenuBar::item {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QMenuBar::item:selected {
|
||||
background-color: @{surface0};
|
||||
}
|
||||
|
||||
QMenu {
|
||||
background-color: @{mantle};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
QMenu::item {
|
||||
padding: 6px 24px 6px 12px;
|
||||
border-radius: 4px;
|
||||
color: @{text};
|
||||
}
|
||||
|
||||
QMenu::item:selected {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
}
|
||||
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background: @{surface1};
|
||||
margin: 4px 8px;
|
||||
}
|
||||
|
||||
/* ── Status bar ─────────────────────────────────────────────────────────── */
|
||||
QStatusBar {
|
||||
background-color: @{crust};
|
||||
border-top: 1px solid @{surface0};
|
||||
color: @{subtext};
|
||||
font-size: 9pt;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
/* ── Scrollbars ─────────────────────────────────────────────────────────── */
|
||||
QScrollBar:vertical {
|
||||
background: @{base};
|
||||
width: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical {
|
||||
background: @{surface1};
|
||||
border-radius: 5px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical:hover {
|
||||
background: @{surface2};
|
||||
}
|
||||
|
||||
QScrollBar:horizontal {
|
||||
background: @{base};
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:horizontal {
|
||||
background: @{surface1};
|
||||
border-radius: 5px;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:horizontal:hover {
|
||||
background: @{surface2};
|
||||
}
|
||||
|
||||
QScrollBar::add-line, QScrollBar::sub-line { height: 0; width: 0; }
|
||||
QScrollBar::add-page, QScrollBar::sub-page { background: transparent; }
|
||||
|
||||
/* ── Splitter ────────────────────────────────────────────────────────────── */
|
||||
QSplitter::handle {
|
||||
background-color: @{surface0};
|
||||
}
|
||||
|
||||
QSplitter::handle:horizontal { width: 2px; }
|
||||
QSplitter::handle:vertical { height: 2px; }
|
||||
|
||||
QSplitter::handle:hover {
|
||||
background-color: @{blue};
|
||||
}
|
||||
|
||||
/* ── Sidebar ─────────────────────────────────────────────────────────────── */
|
||||
#sidebarHeader {
|
||||
background-color: @{mantle};
|
||||
border-bottom: 1px solid @{surface0};
|
||||
}
|
||||
|
||||
#sidebarTitle {
|
||||
color: @{blue};
|
||||
font-size: 11pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#newConnBtn {
|
||||
background-color: @{surface0};
|
||||
color: @{blue};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
font-size: 14pt;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#newConnBtn:hover {
|
||||
background-color: @{surface1};
|
||||
color: @{lavender};
|
||||
}
|
||||
|
||||
/* ── Tree widget ─────────────────────────────────────────────────────────── */
|
||||
QTreeWidget {
|
||||
background-color: @{mantle};
|
||||
border: none;
|
||||
color: @{text};
|
||||
font-size: 10pt;
|
||||
show-decoration-selected: 1;
|
||||
}
|
||||
|
||||
QTreeWidget::item {
|
||||
padding: 3px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QTreeWidget::item:hover {
|
||||
background-color: @{tree_hover};
|
||||
}
|
||||
|
||||
QTreeWidget::item:selected {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
}
|
||||
|
||||
QTreeWidget::branch {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* ── Tab widget ──────────────────────────────────────────────────────────── */
|
||||
QTabWidget::pane {
|
||||
border: none;
|
||||
border-top: 1px solid @{surface0};
|
||||
background-color: @{base};
|
||||
}
|
||||
|
||||
QTabBar {
|
||||
background-color: @{mantle};
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
background-color: @{mantle};
|
||||
color: @{subtext};
|
||||
padding: 7px 16px;
|
||||
border: none;
|
||||
border-right: 1px solid @{surface0};
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
QTabBar::tab:selected {
|
||||
background-color: @{base};
|
||||
color: @{text};
|
||||
border-bottom: 2px solid @{blue};
|
||||
}
|
||||
|
||||
QTabBar::tab:hover:!selected {
|
||||
background-color: @{tab_hover};
|
||||
color: @{text};
|
||||
}
|
||||
|
||||
QTabBar::close-button {
|
||||
subcontrol-position: right;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/* ── Table view ──────────────────────────────────────────────────────────── */
|
||||
QTableView, QTableWidget {
|
||||
background-color: @{base};
|
||||
alternate-background-color: @{tree_hover};
|
||||
gridline-color: @{surface0};
|
||||
color: @{text};
|
||||
border: none;
|
||||
selection-background-color: @{surface0};
|
||||
selection-color: @{text};
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
QTableView::item, QTableWidget::item {
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
QHeaderView {
|
||||
background-color: @{mantle};
|
||||
}
|
||||
|
||||
QHeaderView::section {
|
||||
background-color: @{mantle};
|
||||
color: @{blue};
|
||||
border: none;
|
||||
border-right: 1px solid @{surface0};
|
||||
border-bottom: 1px solid @{surface0};
|
||||
padding: 4px 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
QHeaderView::section:hover {
|
||||
background-color: @{tab_hover};
|
||||
}
|
||||
|
||||
/* ── Plain text edit (SQL editor body) ───────────────────────────────────── */
|
||||
QPlainTextEdit {
|
||||
background-color: @{base};
|
||||
color: @{text};
|
||||
border: none;
|
||||
selection-background-color: @{surface1};
|
||||
font-family: "Consolas", "JetBrains Mono", "Courier New", monospace;
|
||||
font-size: 13pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* ── Line number gutter ──────────────────────────────────────────────────── */
|
||||
LineNumberArea {
|
||||
background-color: @{gutter_bg};
|
||||
}
|
||||
|
||||
/* ── Buttons ─────────────────────────────────────────────────────────────── */
|
||||
QPushButton {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
padding: 5px 14px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: @{surface1};
|
||||
border-color: @{surface2};
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: @{btn_pressed};
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
color: @{surface2};
|
||||
background-color: @{btn_pressed};
|
||||
border-color: @{surface0};
|
||||
}
|
||||
|
||||
#runBtn {
|
||||
background-color: @{green};
|
||||
color: @{base};
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#runBtn:hover {
|
||||
background-color: @{teal};
|
||||
}
|
||||
|
||||
#stopBtn {
|
||||
background-color: @{red};
|
||||
color: @{base};
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#stopBtn:hover {
|
||||
background-color: @{red_hover};
|
||||
}
|
||||
|
||||
#commitBtn {
|
||||
background-color: @{green};
|
||||
color: @{base};
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#rollbackBtn {
|
||||
background-color: @{peach};
|
||||
color: @{base};
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#crudAddBtn {
|
||||
background-color: @{surface0};
|
||||
color: @{green};
|
||||
border: 1px solid @{green};
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#crudAddBtn:hover {
|
||||
background-color: @{green};
|
||||
color: @{base};
|
||||
}
|
||||
|
||||
#crudDeleteBtn {
|
||||
background-color: @{surface0};
|
||||
color: @{red};
|
||||
border: 1px solid @{red};
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#crudDeleteBtn:hover {
|
||||
background-color: @{red};
|
||||
color: @{base};
|
||||
}
|
||||
|
||||
/* ── Line edit ───────────────────────────────────────────────────────────── */
|
||||
QLineEdit {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
padding: 5px 10px;
|
||||
font-size: 10pt;
|
||||
selection-background-color: @{blue};
|
||||
selection-color: @{base};
|
||||
}
|
||||
|
||||
QLineEdit:focus {
|
||||
border-color: @{blue};
|
||||
}
|
||||
|
||||
QLineEdit::placeholder {
|
||||
color: @{surface2};
|
||||
}
|
||||
|
||||
/* ── Combo box ───────────────────────────────────────────────────────────── */
|
||||
QComboBox {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
padding: 5px 10px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
QComboBox:focus {
|
||||
border-color: @{blue};
|
||||
}
|
||||
|
||||
QComboBox::drop-down {
|
||||
border: none;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView {
|
||||
background-color: @{mantle};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
selection-background-color: @{surface0};
|
||||
}
|
||||
|
||||
/* ── Spin box ────────────────────────────────────────────────────────────── */
|
||||
QSpinBox {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
QSpinBox:focus { border-color: @{blue}; }
|
||||
|
||||
QSpinBox::up-button, QSpinBox::down-button {
|
||||
background-color: @{surface1};
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
/* ── Check box ───────────────────────────────────────────────────────────── */
|
||||
QCheckBox {
|
||||
color: @{text};
|
||||
spacing: 8px;
|
||||
}
|
||||
|
||||
QCheckBox::indicator {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid @{surface1};
|
||||
border-radius: 4px;
|
||||
background: @{surface0};
|
||||
}
|
||||
|
||||
QCheckBox::indicator:checked {
|
||||
background-color: @{blue};
|
||||
border-color: @{blue};
|
||||
}
|
||||
|
||||
/* ── Dialog ──────────────────────────────────────────────────────────────── */
|
||||
QDialog {
|
||||
background-color: @{base};
|
||||
}
|
||||
|
||||
QDialogButtonBox QPushButton {
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
/* ── Form layout labels ──────────────────────────────────────────────────── */
|
||||
QFormLayout QLabel {
|
||||
color: @{subtext};
|
||||
}
|
||||
|
||||
/* ── Tab widget in dialogs ───────────────────────────────────────────────── */
|
||||
QTabWidget#dialogTabs::pane {
|
||||
border: 1px solid @{surface0};
|
||||
border-radius: 6px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* ── Group box ───────────────────────────────────────────────────────────── */
|
||||
QGroupBox {
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
margin-top: 1em;
|
||||
color: @{subtext};
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* ── Progress bar ────────────────────────────────────────────────────────── */
|
||||
QProgressBar {
|
||||
background-color: @{surface0};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 6px;
|
||||
height: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QProgressBar::chunk {
|
||||
background-color: @{blue};
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* ── Dock widget ─────────────────────────────────────────────────────────── */
|
||||
QDockWidget {
|
||||
color: @{text};
|
||||
titlebar-close-icon: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
QDockWidget::title {
|
||||
background-color: @{mantle};
|
||||
padding: 6px;
|
||||
border-bottom: 1px solid @{surface0};
|
||||
}
|
||||
|
||||
/* ── Tool button ─────────────────────────────────────────────────────────── */
|
||||
QToolButton {
|
||||
background-color: @{surface0};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 5px;
|
||||
padding: 4px 8px;
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
QToolButton:hover {
|
||||
background-color: @{surface1};
|
||||
}
|
||||
|
||||
/* ── Message box ─────────────────────────────────────────────────────────── */
|
||||
QMessageBox {
|
||||
background-color: @{base};
|
||||
}
|
||||
|
||||
QMessageBox QLabel {
|
||||
color: @{text};
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
/* ── Empty workspace label ───────────────────────────────────────────────── */
|
||||
#emptyLabel {
|
||||
color: @{surface2};
|
||||
font-size: 14pt;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
/* ── Status label in results toolbar ────────────────────────────────────── */
|
||||
#statusLabel {
|
||||
color: @{subtext};
|
||||
font-size: 9pt;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
/* ── DB label in SQL editor toolbar ─────────────────────────────────────── */
|
||||
#dbLabel {
|
||||
color: @{overlay};
|
||||
font-size: 9pt;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* ── Workspace tab widget ────────────────────────────────────────────────── */
|
||||
#workspace > QTabBar::tab {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* ── Horizontal separator line in dialog ────────────────────────────────── */
|
||||
QFrame[frameShape="4"] { /* HLine */
|
||||
color: @{surface0};
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* ── Structure view title ────────────────────────────────────────────────── */
|
||||
#structureTitle {
|
||||
color: @{lavender};
|
||||
}
|
||||
|
||||
/* ── Pagination bar ──────────────────────────────────────────────────────── */
|
||||
#paginationBar {
|
||||
background-color: @{mantle};
|
||||
border-top: 1px solid @{surface1};
|
||||
min-height: 38px;
|
||||
}
|
||||
|
||||
#pgBtn {
|
||||
background-color: @{btn_pressed};
|
||||
color: @{text};
|
||||
border: 1px solid @{surface1};
|
||||
border-radius: 5px;
|
||||
padding: 4px 10px;
|
||||
font-size: 9pt;
|
||||
min-width: 52px;
|
||||
}
|
||||
|
||||
#pgBtn:hover {
|
||||
background-color: @{surface0};
|
||||
border-color: @{surface2};
|
||||
}
|
||||
|
||||
#pgBtn:disabled {
|
||||
color: @{surface1};
|
||||
background-color: @{base};
|
||||
border-color: @{surface0};
|
||||
}
|
||||
|
||||
#pageLbl {
|
||||
color: @{blue};
|
||||
font-size: 9pt;
|
||||
font-weight: 600;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
#rowCountLbl {
|
||||
color: @{subtext};
|
||||
font-size: 9pt;
|
||||
padding-left: 8px;
|
||||
}
|
||||
Reference in New Issue
Block a user