feat: dark/light theme toggle (Catppuccin Frappé ↔ Latte)
- app/config/theme.py: palette definitions for both themes + apply_qss() helper used by both main.py (startup) and MainWindow (live toggle) - resources/style_light.qss: complete Catppuccin Latte QSS - View menu: ☀️/🌙 Switch Theme action (Ctrl+Shift+T) toggles live - Preferences → Appearance: Color theme dropdown persists choice - SQLHighlighter: _build_rules() reads get_palette(); update_theme() rebuilds rules and rehighlights all open editors on switch - SqlCompleter: popup stylesheet reads get_palette() via _apply_popup_style(); update_theme() called on switch - EditableTableModel: dirty/delete cell colors read get_palette() live - ResultTableModel: NULL_COLOR property reads get_palette() live - TableViewer: apply_settings() refreshes frozen-view border + repaints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-6
@@ -22,6 +22,7 @@ from PyQt6.QtGui import QColor, QBrush, QFont, QKeySequence, QShortcut
|
||||
|
||||
from app.utils.worker import TableDataWorker
|
||||
from app.config.settings import get_settings
|
||||
from app.config.theme import get_palette
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -148,18 +149,20 @@ class EditableTableModel(QAbstractTableModel):
|
||||
return "" if val is None else str(val)
|
||||
|
||||
if role == Qt.ItemDataRole.ForegroundRole:
|
||||
p = get_palette()
|
||||
if self._is_deleted_row(r):
|
||||
return QBrush(QColor("#e78284")) # red — pending delete
|
||||
return QBrush(QColor(p["delete_fg"]))
|
||||
if (r, c) in self._dirty:
|
||||
return QBrush(QColor("#ef9f76")) # orange — edited
|
||||
return QBrush(QColor(p["dirty_fg"]))
|
||||
if val is None:
|
||||
return QBrush(QColor("#737994")) # grey — NULL
|
||||
return QBrush(QColor(p["overlay"]))
|
||||
|
||||
if role == Qt.ItemDataRole.BackgroundRole:
|
||||
p = get_palette()
|
||||
if self._is_deleted_row(r):
|
||||
return QBrush(QColor("#3a2530"))
|
||||
return QBrush(QColor(p["delete_bg"]))
|
||||
if (r, c) in self._dirty:
|
||||
return QBrush(QColor("#3a2f25"))
|
||||
return QBrush(QColor(p["dirty_bg"]))
|
||||
|
||||
return None
|
||||
|
||||
@@ -444,7 +447,7 @@ class TableViewer(QWidget):
|
||||
self._frozen_view.horizontalHeader().setSectionResizeMode(
|
||||
QHeaderView.ResizeMode.Fixed)
|
||||
self._frozen_view.setStyleSheet(
|
||||
"QTableView { border: none; border-right: 2px solid #8caaee; }")
|
||||
f"QTableView {{ border: none; border-right: 2px solid {get_palette()['blue']}; }}")
|
||||
self._frozen_view.hide()
|
||||
|
||||
# Sync vertical scroll between the two views
|
||||
@@ -887,6 +890,12 @@ class TableViewer(QWidget):
|
||||
)
|
||||
QApplication.clipboard().setText("\n".join(statements))
|
||||
|
||||
def apply_settings(self):
|
||||
"""Called by MainWindow when preferences (including theme) change."""
|
||||
self._frozen_view.setStyleSheet(
|
||||
f"QTableView {{ border: none; border-right: 2px solid {get_palette()['blue']}; }}")
|
||||
self._model.layoutChanged.emit() # repaint dirty/delete cells
|
||||
|
||||
def _filter_by_cell(self, index):
|
||||
if not index.isValid():
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user