CmdForge/src/cmdforge/gui/styles.py

938 lines
16 KiB
Python

"""Theme stylesheets for CmdForge GUI."""
from pathlib import Path
# Light theme (default)
LIGHT_THEME = """
/* Main Window */
QMainWindow {
background-color: #f5f5f5;
}
/* Sidebar */
#sidebar {
background-color: #2d3748;
border: none;
min-width: 180px;
max-width: 180px;
}
#sidebar::item {
color: #e2e8f0;
padding: 12px 16px;
border: none;
border-left: 3px solid transparent;
}
#sidebar::item:selected {
background-color: #4a5568;
border-left: 3px solid #667eea;
color: white;
}
#sidebar::item:hover:!selected {
background-color: #3d4a5c;
}
/* Content area */
QWidget#content_area {
background-color: #f7fafc;
}
/* Buttons */
QPushButton {
background-color: #667eea;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-weight: 500;
min-height: 32px;
}
QPushButton:hover {
background-color: #5a67d8;
}
QPushButton:pressed {
background-color: #4c51bf;
}
QPushButton:disabled {
background-color: #a0aec0;
}
QPushButton#secondary {
background-color: #e2e8f0;
color: #4a5568;
}
QPushButton#secondary:hover {
background-color: #cbd5e0;
}
QPushButton#danger {
background-color: #e53e3e;
}
QPushButton#danger:hover {
background-color: #c53030;
}
/* View toggle buttons */
QPushButton#viewToggle {
background-color: #e2e8f0;
color: #4a5568;
border-radius: 4px;
padding: 6px 12px;
min-height: 24px;
min-width: 60px;
}
QPushButton#viewToggle:checked {
background-color: #667eea;
color: white;
}
QPushButton#viewToggle:hover:!checked {
background-color: #cbd5e0;
}
/* Input fields */
QLineEdit, QTextEdit, QPlainTextEdit {
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 8px 12px;
background-color: white;
selection-background-color: #667eea;
}
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {
border-color: #667eea;
outline: none;
}
/* Combo boxes */
QComboBox {
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 8px 12px;
background-color: white;
min-height: 20px;
}
QComboBox:focus {
border-color: #667eea;
}
QComboBox::drop-down {
border: none;
width: 24px;
}
QComboBox::down-arrow {
image: none;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid #718096;
margin-right: 8px;
}
QComboBox QAbstractItemView {
border: 1px solid #e2e8f0;
background-color: white;
selection-background-color: #667eea;
}
/* Lists and Trees */
QListWidget, QTreeWidget, QTableWidget {
border: 1px solid #e2e8f0;
border-radius: 6px;
background-color: white;
outline: none;
}
QListWidget::item, QTreeWidget::item {
padding: 8px 12px;
border: none;
}
QListWidget::item:selected, QTreeWidget::item:selected {
background-color: #667eea;
color: white;
}
QListWidget::item:hover:!selected, QTreeWidget::item:hover:!selected {
background-color: #edf2f7;
}
/* Table Widget */
QTableWidget {
gridline-color: #e2e8f0;
}
QTableWidget::item {
padding: 8px;
}
QTableWidget::item:selected {
background-color: #667eea;
color: white;
}
QHeaderView::section {
background-color: #f7fafc;
padding: 8px 12px;
border: none;
border-bottom: 1px solid #e2e8f0;
font-weight: 600;
color: #4a5568;
}
/* Group boxes */
QGroupBox {
font-weight: 600;
border: 1px solid #e2e8f0;
border-radius: 8px;
margin-top: 12px;
padding-top: 12px;
background-color: white;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
left: 12px;
padding: 0 8px;
color: #2d3748;
}
/* Labels */
QLabel {
color: #2d3748;
}
QLabel#heading {
font-size: 18px;
font-weight: 600;
color: #1a202c;
}
QLabel#subheading {
font-size: 14px;
color: #718096;
}
QLabel#label {
font-weight: 500;
color: #4a5568;
}
QLabel#sectionHeading {
font-weight: 600;
font-size: 13px;
color: #2d3748;
}
/* Scrollbars */
QScrollBar:vertical {
background-color: #f7fafc;
width: 12px;
border-radius: 6px;
}
QScrollBar::handle:vertical {
background-color: #cbd5e0;
border-radius: 6px;
min-height: 30px;
}
QScrollBar::handle:vertical:hover {
background-color: #a0aec0;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0;
}
QScrollBar:horizontal {
background-color: #f7fafc;
height: 12px;
border-radius: 6px;
}
QScrollBar::handle:horizontal {
background-color: #cbd5e0;
border-radius: 6px;
min-width: 30px;
}
QScrollBar::handle:horizontal:hover {
background-color: #a0aec0;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
width: 0;
}
/* Status bar */
QStatusBar {
background-color: #f7fafc;
border-top: 1px solid #e2e8f0;
color: #718096;
}
/* Splitter */
QSplitter::handle {
background-color: #e2e8f0;
}
QSplitter::handle:horizontal {
width: 2px;
}
QSplitter::handle:vertical {
height: 2px;
}
/* Tabs */
QTabWidget::pane {
border: 1px solid #e2e8f0;
border-radius: 6px;
background-color: white;
}
QTabBar::tab {
background-color: #edf2f7;
border: 1px solid #e2e8f0;
border-bottom: none;
padding: 8px 16px;
margin-right: 2px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
QTabBar::tab:selected {
background-color: white;
border-bottom: 1px solid white;
}
QTabBar::tab:hover:!selected {
background-color: #e2e8f0;
}
/* Dialogs */
QDialog {
background-color: #f7fafc;
}
/* Message boxes */
QMessageBox {
background-color: #f7fafc;
}
QMessageBox QPushButton {
min-width: 80px;
}
/* Tool tips */
QToolTip {
background-color: #2d3748;
color: white;
border: none;
padding: 6px 10px;
border-radius: 4px;
}
/* Progress bar */
QProgressBar {
border: 1px solid #e2e8f0;
border-radius: 6px;
background-color: #edf2f7;
text-align: center;
height: 20px;
}
QProgressBar::chunk {
background-color: #667eea;
border-radius: 5px;
}
/* Spin boxes */
QSpinBox, QDoubleSpinBox {
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 8px 12px;
background-color: white;
}
QSpinBox:focus, QDoubleSpinBox:focus {
border-color: #667eea;
}
/* Check boxes and radio buttons */
QCheckBox, QRadioButton {
spacing: 8px;
color: #2d3748;
}
QCheckBox::indicator, QRadioButton::indicator {
width: 18px;
height: 18px;
}
QCheckBox::indicator:unchecked {
border: 2px solid #cbd5e0;
border-radius: 4px;
background-color: white;
}
QCheckBox::indicator:checked {
border: 2px solid #667eea;
border-radius: 4px;
background-color: #667eea;
}
QRadioButton::indicator:unchecked {
border: 2px solid #cbd5e0;
border-radius: 9px;
background-color: white;
}
QRadioButton::indicator:checked {
border: 2px solid #667eea;
border-radius: 9px;
background-color: #667eea;
}
/* Menu bar */
QMenuBar {
background-color: #f7fafc;
border-bottom: 1px solid #e2e8f0;
}
QMenuBar::item {
padding: 6px 12px;
background-color: transparent;
}
QMenuBar::item:selected {
background-color: #e2e8f0;
}
QMenu {
background-color: white;
border: 1px solid #e2e8f0;
}
QMenu::item {
padding: 8px 24px;
}
QMenu::item:selected {
background-color: #667eea;
color: white;
}
QMenu::separator {
height: 1px;
background-color: #e2e8f0;
margin: 4px 8px;
}
"""
# Dark theme
DARK_THEME = """
/* Main Window */
QMainWindow {
background-color: #1a1a2e;
}
/* Sidebar */
#sidebar {
background-color: #16213e;
border: none;
min-width: 180px;
max-width: 180px;
}
#sidebar::item {
color: #a0aec0;
padding: 12px 16px;
border: none;
border-left: 3px solid transparent;
}
#sidebar::item:selected {
background-color: #1f3460;
border-left: 3px solid #667eea;
color: white;
}
#sidebar::item:hover:!selected {
background-color: #1a2d50;
}
/* Content area */
QWidget#content_area {
background-color: #1a1a2e;
}
/* Buttons */
QPushButton {
background-color: #667eea;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-weight: 500;
min-height: 32px;
}
QPushButton:hover {
background-color: #7c8ff0;
}
QPushButton:pressed {
background-color: #5a67d8;
}
QPushButton:disabled {
background-color: #4a5568;
color: #718096;
}
QPushButton#secondary {
background-color: #2d3748;
color: #e2e8f0;
}
QPushButton#secondary:hover {
background-color: #4a5568;
}
QPushButton#danger {
background-color: #e53e3e;
}
QPushButton#danger:hover {
background-color: #fc5c65;
}
/* View toggle buttons */
QPushButton#viewToggle {
background-color: #2d3748;
color: #a0aec0;
border-radius: 4px;
padding: 6px 12px;
min-height: 24px;
min-width: 60px;
}
QPushButton#viewToggle:checked {
background-color: #667eea;
color: white;
}
QPushButton#viewToggle:hover:!checked {
background-color: #4a5568;
}
/* Input fields */
QLineEdit, QTextEdit, QPlainTextEdit {
border: 1px solid #4a5568;
border-radius: 6px;
padding: 8px 12px;
background-color: #2d3748;
color: #e2e8f0;
selection-background-color: #667eea;
}
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {
border-color: #667eea;
outline: none;
}
/* Combo boxes */
QComboBox {
border: 1px solid #4a5568;
border-radius: 6px;
padding: 8px 12px;
background-color: #2d3748;
color: #e2e8f0;
min-height: 20px;
}
QComboBox:focus {
border-color: #667eea;
}
QComboBox::drop-down {
border: none;
width: 24px;
}
QComboBox::down-arrow {
image: none;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid #a0aec0;
margin-right: 8px;
}
QComboBox QAbstractItemView {
border: 1px solid #4a5568;
background-color: #2d3748;
color: #e2e8f0;
selection-background-color: #667eea;
}
/* Lists and Trees */
QListWidget, QTreeWidget, QTableWidget {
border: 1px solid #4a5568;
border-radius: 6px;
background-color: #2d3748;
color: #e2e8f0;
outline: none;
}
QListWidget::item, QTreeWidget::item {
padding: 8px 12px;
border: none;
}
QListWidget::item:selected, QTreeWidget::item:selected {
background-color: #667eea;
color: white;
}
QListWidget::item:hover:!selected, QTreeWidget::item:hover:!selected {
background-color: #4a5568;
}
/* Table Widget */
QTableWidget {
gridline-color: #4a5568;
}
QTableWidget::item {
padding: 8px;
}
QTableWidget::item:selected {
background-color: #667eea;
color: white;
}
QHeaderView::section {
background-color: #1a1a2e;
padding: 8px 12px;
border: none;
border-bottom: 1px solid #4a5568;
font-weight: 600;
color: #a0aec0;
}
/* Group boxes */
QGroupBox {
font-weight: 600;
border: 1px solid #4a5568;
border-radius: 8px;
margin-top: 12px;
padding-top: 12px;
background-color: #2d3748;
color: #e2e8f0;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
left: 12px;
padding: 0 8px;
color: #e2e8f0;
}
/* Labels */
QLabel {
color: #e2e8f0;
}
QLabel#heading {
font-size: 18px;
font-weight: 600;
color: #ffffff;
}
QLabel#subheading {
font-size: 14px;
color: #a0aec0;
}
QLabel#label {
font-weight: 500;
color: #cbd5e0;
}
QLabel#sectionHeading {
font-weight: 600;
font-size: 13px;
color: #e2e8f0;
}
/* Scrollbars */
QScrollBar:vertical {
background-color: #1a1a2e;
width: 12px;
border-radius: 6px;
}
QScrollBar::handle:vertical {
background-color: #4a5568;
border-radius: 6px;
min-height: 30px;
}
QScrollBar::handle:vertical:hover {
background-color: #718096;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0;
}
QScrollBar:horizontal {
background-color: #1a1a2e;
height: 12px;
border-radius: 6px;
}
QScrollBar::handle:horizontal {
background-color: #4a5568;
border-radius: 6px;
min-width: 30px;
}
QScrollBar::handle:horizontal:hover {
background-color: #718096;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
width: 0;
}
/* Status bar */
QStatusBar {
background-color: #16213e;
border-top: 1px solid #4a5568;
color: #a0aec0;
}
/* Splitter */
QSplitter::handle {
background-color: #4a5568;
}
QSplitter::handle:horizontal {
width: 2px;
}
QSplitter::handle:vertical {
height: 2px;
}
/* Tabs */
QTabWidget::pane {
border: 1px solid #4a5568;
border-radius: 6px;
background-color: #2d3748;
}
QTabBar::tab {
background-color: #1a1a2e;
border: 1px solid #4a5568;
border-bottom: none;
padding: 8px 16px;
margin-right: 2px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
color: #a0aec0;
}
QTabBar::tab:selected {
background-color: #2d3748;
border-bottom: 1px solid #2d3748;
color: #e2e8f0;
}
QTabBar::tab:hover:!selected {
background-color: #16213e;
}
/* Dialogs */
QDialog {
background-color: #1a1a2e;
}
/* Message boxes */
QMessageBox {
background-color: #1a1a2e;
}
QMessageBox QPushButton {
min-width: 80px;
}
/* Tool tips */
QToolTip {
background-color: #4a5568;
color: white;
border: none;
padding: 6px 10px;
border-radius: 4px;
}
/* Progress bar */
QProgressBar {
border: 1px solid #4a5568;
border-radius: 6px;
background-color: #2d3748;
text-align: center;
height: 20px;
color: #e2e8f0;
}
QProgressBar::chunk {
background-color: #667eea;
border-radius: 5px;
}
/* Spin boxes */
QSpinBox, QDoubleSpinBox {
border: 1px solid #4a5568;
border-radius: 6px;
padding: 8px 12px;
background-color: #2d3748;
color: #e2e8f0;
}
QSpinBox:focus, QDoubleSpinBox:focus {
border-color: #667eea;
}
/* Check boxes and radio buttons */
QCheckBox, QRadioButton {
spacing: 8px;
color: #e2e8f0;
}
QCheckBox::indicator, QRadioButton::indicator {
width: 18px;
height: 18px;
}
QCheckBox::indicator:unchecked {
border: 2px solid #4a5568;
border-radius: 4px;
background-color: #2d3748;
}
QCheckBox::indicator:checked {
border: 2px solid #667eea;
border-radius: 4px;
background-color: #667eea;
}
QRadioButton::indicator:unchecked {
border: 2px solid #4a5568;
border-radius: 9px;
background-color: #2d3748;
}
QRadioButton::indicator:checked {
border: 2px solid #667eea;
border-radius: 9px;
background-color: #667eea;
}
/* Menu bar */
QMenuBar {
background-color: #16213e;
border-bottom: 1px solid #4a5568;
color: #e2e8f0;
}
QMenuBar::item {
padding: 6px 12px;
background-color: transparent;
}
QMenuBar::item:selected {
background-color: #1f3460;
}
QMenu {
background-color: #2d3748;
border: 1px solid #4a5568;
color: #e2e8f0;
}
QMenu::item {
padding: 8px 24px;
}
QMenu::item:selected {
background-color: #667eea;
color: white;
}
QMenu::separator {
height: 1px;
background-color: #4a5568;
margin: 4px 8px;
}
"""
# Available themes
THEMES = {
"light": LIGHT_THEME,
"dark": DARK_THEME,
}
# For backwards compatibility
STYLESHEET = LIGHT_THEME
def get_custom_theme_path() -> Path:
"""Get path to custom theme file."""
return Path.home() / ".cmdforge" / "theme.qss"
def load_theme(theme_name: str = "light") -> str:
"""
Load a theme stylesheet.
Args:
theme_name: Name of theme ("light", "dark") or "custom" for external file
Returns:
The stylesheet string
"""
# Check for custom theme file first
custom_path = get_custom_theme_path()
if theme_name == "custom" and custom_path.exists():
try:
return custom_path.read_text()
except Exception:
pass # Fall back to light theme
# Return built-in theme
return THEMES.get(theme_name, LIGHT_THEME)
def get_available_themes() -> list:
"""Get list of available theme names."""
themes = list(THEMES.keys())
if get_custom_theme_path().exists():
themes.append("custom")
return themes