From eae3f0cdcedbe6a310523483af66624353b2776c Mon Sep 17 00:00:00 2001 From: Loren Eteval Date: Wed, 19 Aug 2026 22:02:57 +0800 Subject: [PATCH] Split Qt stylesheets by component Signed-off-by: Loren Eteval --- Furious/Qt/AppStyleSheet.py | 1151 +-------------------------- Furious/Qt/StyleSheets/Base.py | 346 ++++++++ Furious/Qt/StyleSheets/Controls.py | 517 ++++++++++++ Furious/Qt/StyleSheets/DataViews.py | 360 +++++++++ Furious/Qt/StyleSheets/__init__.py | 49 ++ 5 files changed, 1282 insertions(+), 1141 deletions(-) create mode 100644 Furious/Qt/StyleSheets/Base.py create mode 100644 Furious/Qt/StyleSheets/Controls.py create mode 100644 Furious/Qt/StyleSheets/DataViews.py create mode 100644 Furious/Qt/StyleSheets/__init__.py diff --git a/Furious/Qt/AppStyleSheet.py b/Furious/Qt/AppStyleSheet.py index ed95207..2710db8 100644 --- a/Furious/Qt/AppStyleSheet.py +++ b/Furious/Qt/AppStyleSheet.py @@ -21,6 +21,8 @@ from __future__ import annotations from textwrap import dedent +from Furious.Qt.StyleSheets import composeStyleSheet + __all__ = ['AppStyleSheet'] @@ -277,1144 +279,11 @@ class AppStyleSheet: caretRightIcon = f'{iconPrefix}/caret-right-fill.svg' checkIcon = ':/Icons/bootstrap/white/check.svg' - return dedent(f""" - * {{ - outline: none; - }} - - QWidget {{ - background-color: {palette['window']}; - color: {palette['text']}; - selection-background-color: {palette['selection']}; - selection-color: {palette['selection_text']}; - }} - - QMainWindow, - QDialog, - QMessageBox {{ - background-color: {palette['window']}; - }} - - QLabel, - QDialogButtonBox, - QSizeGrip {{ - border: none; - background-color: transparent; - }} - - QLabel:disabled {{ - color: {palette['disabled']}; - }} - - QFrame {{ - border-color: {palette['border']}; - }} - - QFrame#NavigationPanel {{ - border: none; - border-right: 1px solid {palette['border']}; - background-color: {palette['panel_alt']}; - }} - - QFrame#NavigationRailPlaceholder {{ - border: none; - background-color: {palette['panel_alt']}; - }} - - QFrame#AppMessageBoxMask {{ - border: none; - background-color: rgba(0, 0, 0, 84); - }} - - QDialog#AppMessageBox {{ - border: none; - background-color: transparent; - }} - - QFrame#AppMessageBoxSurface {{ - border: 1px solid {palette['border_strong']}; - border-radius: 10px; - background-color: {palette['panel']}; - }} - - QFrame#AppMessageBoxContent, - QWidget#AppMessageBoxTextWidget, - QScrollArea#AppMessageBoxTextViewport, - QScrollArea#AppMessageBoxTextViewport > QWidget > QWidget, - QLabel#AppMessageBoxIcon {{ - border: none; - background-color: transparent; - }} - - QLabel#AppMessageBoxTitle {{ - padding: 0; - border: none; - background-color: transparent; - color: {palette['text_strong']}; - }} - - QLabel#AppMessageBoxBody {{ - padding: 0; - border: none; - background-color: transparent; - color: {palette['muted']}; - }} - - QFrame#AppMessageBoxButtonBar {{ - border-top: 1px solid {palette['border']}; - border-left: none; - border-right: none; - border-bottom-left-radius: 10px; - border-bottom-right-radius: 10px; - background-color: {palette['panel_alt']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton {{ - padding: 5px 14px; - border: 1px solid {palette['border']}; - border-radius: 6px; - background-color: {palette['input']}; - color: {palette['text']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton:hover {{ - border-color: {palette['border_strong']}; - background-color: {palette['hover']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton:pressed {{ - background-color: {palette['pressed']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="primary"] {{ - border-color: {palette['accent']}; - background-color: {palette['accent']}; - color: {palette['accent_text']}; - font-weight: 600; - }} - - QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="primary"]:hover {{ - border-color: {palette['accent_hover']}; - background-color: {palette['accent_hover']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="primary"]:pressed {{ - border-color: {palette['accent_pressed']}; - background-color: {palette['accent_pressed']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="destructive"] {{ - border-color: {palette['danger']}; - background-color: {palette['danger_soft']}; - color: {palette['danger']}; - }} - - QFrame#AppMessageBoxButtonBar QPushButton:disabled {{ - border-color: {palette['border']}; - background-color: {palette['raised']}; - color: {palette['disabled']}; - }} - - QStackedWidget#ApplicationPageStack {{ - border: none; - background-color: {palette['window']}; - }} - - QScrollArea#MetricsScrollArea, - QWidget#MetricsPageContent, - QScrollArea#SettingsScrollArea, - QWidget#SettingsPageContent, - QScrollArea#SubscriptionScrollArea, - QWidget#SubscriptionPageContent, - QWidget#HomePageContent, - QWidget#LogPageContent {{ - border: none; - background-color: {palette['window']}; - }} - - QLabel#MetricsPageTitle, - QLabel#LogPageTitle, - QLabel#SubscriptionPageTitle, - QLabel#HomePageTitle, - QLabel#SettingsPageTitle {{ - color: {palette['text_strong']}; - font-size: 16pt; - font-weight: 600; - }} - - QLabel#SettingsSectionTitle {{ - padding: 4px 2px; - color: {palette['text_strong']}; - font-size: 12pt; - font-weight: 600; - }} - - QFrame#SettingsCard {{ - border: 1px solid {palette['border']}; - border-radius: 9px; - background-color: {palette['panel']}; - }} - - QFrame#SettingsCard:hover {{ - border-color: {palette['border_strong']}; - background-color: {palette['panel_alt']}; - }} - - QFrame#SubscriptionEditorForm {{ - border: 1px solid {palette['border']}; - border-radius: 9px; - background-color: {palette['panel']}; - }} - - QLabel#SettingsCardTitle {{ - color: {palette['text_strong']}; - font-weight: 600; - }} - - QLabel#SettingsCardDescription {{ - color: {palette['muted']}; - }} - - QPushButton#SettingsActionButton {{ - min-width: 82px; - }} - - QPushButton#SettingsLinkButton {{ - min-height: 20px; - padding: 0; - border: none; - background-color: transparent; - color: {palette['accent']}; - }} - - QPushButton#SettingsLinkButton:hover {{ - color: {palette['text_strong']}; - }} - - QCheckBox#SettingsToggle::indicator {{ - width: 34px; - height: 18px; - border: 1px solid {palette['border_strong']}; - border-radius: 9px; - background-color: {palette['raised']}; - image: none; - }} - - QCheckBox#SettingsToggle::indicator:hover {{ - border-color: {palette['accent']}; - background-color: {palette['hover']}; - }} - - QCheckBox#SettingsToggle::indicator:checked {{ - border-color: {palette['accent']}; - background-color: {palette['accent']}; - image: none; - }} - - QCheckBox#SettingsToggle::indicator:disabled {{ - border-color: {palette['border']}; - background-color: {palette['raised']}; - image: none; - }} - - QFrame#MetricsSection {{ - border: 1px solid {palette['border']}; - border-radius: 10px; - background-color: {palette['panel']}; - }} - - QLabel#MetricsSectionTitle {{ - color: {palette['text_strong']}; - font-size: 12pt; - font-weight: 600; - }} - - QFrame#MetricCard {{ - border: 1px solid {palette['border']}; - border-radius: 8px; - background-color: {palette['panel_alt']}; - }} - - QLabel#MetricCardTitle {{ - color: {palette['muted']}; - font-weight: 600; - }} - - QLabel#EndpointStatusLabel {{ - color: {palette['text_strong']}; - font-weight: 600; - }} - - QWidget#EndpointStatusWidget {{ - background-color: transparent; - }} - - QLabel#EndpointFieldName {{ - color: {palette['muted']}; - }} - - QLabel#EndpointFieldValue {{ - color: {palette['text_strong']}; - }} - - QWidget#EndpointFieldValueContainer {{ - background-color: transparent; - }} - - QLabel#EndpointNoteLabel {{ - color: {palette['muted']}; - }} - - QPushButton#EndpointCopyButton {{ - min-width: 34px; - padding: 0; - }} - - QWidget#EndpointMapWidget {{ - border: none; - background-color: transparent; - }} - - QWidget#MetricsGraphWidget {{ - border: none; - background-color: transparent; - }} - - QGroupBox {{ - margin-top: 16px; - padding: 14px 12px 12px 12px; - border: 1px solid {palette['border']}; - border-radius: 8px; - background-color: {palette['panel']}; - }} - - QGroupBox::title {{ - subcontrol-origin: margin; - subcontrol-position: top left; - left: 10px; - padding: 0 6px; - background-color: {palette['panel']}; - color: {palette['muted']}; - font-weight: 600; - }} - - QToolTip {{ - padding: 7px 10px; - border: 1px solid {palette['border_strong']}; - border-radius: 6px; - background-color: {palette['overlay']}; - color: {palette['text_strong']}; - }} - - QMenuBar {{ - padding: 2px 5px; - border: none; - border-bottom: 1px solid {palette['border']}; - background-color: {palette['panel']}; - spacing: 2px; - }} - - QMenuBar::item {{ - min-height: 22px; - padding: 6px 10px; - border-radius: 5px; - background-color: transparent; - }} - - QMenuBar::item:selected {{ - background-color: {palette['hover']}; - color: {palette['text_strong']}; - }} - - QMenuBar::item:pressed {{ - background-color: {palette['pressed']}; - }} - - QMenu {{ - padding: 6px; - border: 1px solid {palette['border_strong']}; - border-radius: 9px; - background-color: {palette['overlay']}; - }} - - QMenu::separator {{ - height: 1px; - margin: 6px 9px; - background-color: {palette['border_strong']}; - }} - - QMenu::item {{ - min-width: 160px; - min-height: 22px; - padding: 7px 32px; - border-radius: 6px; - background-color: transparent; - }} - - QMenu::item:selected {{ - background-color: {palette['accent_soft_hover']}; - color: {palette['text_strong']}; - }} - - QMenu::item:pressed {{ - background-color: {palette['pressed']}; - color: {palette['text_strong']}; - }} - - QMenu::item:disabled {{ - background-color: transparent; - color: {palette['disabled']}; - }} - - QMenu::item:disabled:selected {{ - background-color: transparent; - }} - - QMenu::icon {{ - position: relative; - left: 8px; - }} - - QMenu::indicator {{ - width: 14px; - height: 14px; - position: relative; - left: 8px; - }} - - QMenu::indicator:checked {{ - border-radius: 4px; - background-color: {palette['accent']}; - image: url("{checkIcon}"); - }} - - QMenu::right-arrow {{ - width: 9px; - height: 9px; - position: relative; - right: 8px; - image: url("{caretRightIcon}"); - }} - - QMenu[menuRole="tray"] {{ - padding: 7px; - border-radius: 10px; - }} - - QMenu[menuRole="tray"]::item {{ - min-width: 176px; - min-height: 24px; - padding: 8px 34px; - }} - - QMenu[menuRole="tray"]::separator {{ - margin: 6px 10px; - }} - - QMenu[menuRole="tray"]::icon, - QMenu[menuRole="tray"]::indicator {{ - left: 9px; - }} - - QMenu[menuRole="tray"]::right-arrow {{ - right: 9px; - }} - - QToolBar {{ - padding: 4px 6px; - border: none; - border-bottom: 1px solid {palette['border']}; - background-color: {palette['panel']}; - spacing: 3px; - }} - - QToolBar::separator {{ - width: 1px; - margin: 5px 5px; - background-color: {palette['border']}; - }} - - QToolBar#AppMainWindow_AppQToolBar, - QToolBar#HomePageToolBar {{ - padding: 6px 8px; - spacing: 2px; - background-color: {palette['panel']}; - }} - - QToolBar#AppMainWindow_AppQToolBar::separator, - QToolBar#HomePageToolBar::separator {{ - margin: 8px 4px; - }} - - QToolButton {{ - min-height: 24px; - padding: 5px 8px; - border: 1px solid transparent; - border-radius: 6px; - background-color: transparent; - color: {palette['text']}; - }} - - QToolBar#AppMainWindow_AppQToolBar QToolButton, - QToolBar#HomePageToolBar QToolButton {{ - min-width: 72px; - min-height: 54px; - padding: 5px 11px; - border-radius: 8px; - }} - - QPushButton#NavigationToggleButton, - QPushButton#NavigationPageButton {{ - min-width: 0; - min-height: 38px; - padding: 0; - border: 1px solid transparent; - border-radius: 7px; - background-color: transparent; - color: {palette['text']}; - text-align: left; - }} - - QPushButton#NavigationToggleButton:hover, - QPushButton#NavigationPageButton:hover {{ - border-color: transparent; - background-color: {palette['hover']}; - color: {palette['text_strong']}; - }} - - QPushButton#NavigationPageButton:checked {{ - border: none; - border-radius: 7px; - background-color: {palette['accent_soft']}; - color: {palette['text_strong']}; - font-weight: 600; - }} - - QFrame#NavigationSelectionIndicator {{ - border: none; - border-radius: 0; - background-color: {palette['accent']}; - }} - - QPushButton#NavigationPageButton:checked:hover {{ - background-color: {palette['accent_soft_hover']}; - }} - - QPushButton#NavigationPageButton QLabel {{ - border: none; - background-color: transparent; - color: {palette['text']}; - }} - - QPushButton#NavigationPageButton:checked QLabel {{ - color: {palette['text_strong']}; - font-weight: 600; - }} - - QPushButton#SearchButton {{ - padding: 0; - }} - - QToolButton:hover {{ - border-color: {palette['border']}; - background-color: {palette['hover']}; - color: {palette['text_strong']}; - }} - - QToolButton:pressed {{ - border-color: {palette['border_strong']}; - background-color: {palette['pressed']}; - }} - - QToolButton:checked {{ - border-color: {palette['accent']}; - background-color: {palette['accent_soft']}; - color: {palette['text_strong']}; - }} - - QToolButton:checked:hover {{ - background-color: {palette['accent_soft_hover']}; - }} - - QToolButton:disabled {{ - border-color: transparent; - background-color: transparent; - color: {palette['disabled']}; - }} - - QPushButton {{ - min-height: 28px; - padding: 3px 13px; - border: 1px solid {palette['border_strong']}; - border-radius: 6px; - background-color: {palette['panel']}; - color: {palette['text']}; - }} - - QPushButton:hover {{ - border-color: {palette['accent']}; - background-color: {palette['hover']}; - color: {palette['text_strong']}; - }} - - QPushButton:focus {{ - border: 1px solid {palette['accent']}; - }} - - QPushButton:pressed {{ - border-color: {palette['accent_pressed']}; - background-color: {palette['pressed']}; - }} - - QPushButton:default {{ - border-color: {palette['accent']}; - background-color: {palette['accent']}; - color: {palette['accent_text']}; - font-weight: 600; - }} - - QPushButton:default:hover {{ - border-color: {palette['accent_hover']}; - background-color: {palette['accent_hover']}; - }} - - QPushButton:default:pressed {{ - border-color: {palette['accent_pressed']}; - background-color: {palette['accent_pressed']}; - }} - - QPushButton:flat {{ - border-color: transparent; - background-color: transparent; - }} - - QPushButton:disabled {{ - border-color: {palette['border']}; - background-color: {palette['raised']}; - color: {palette['disabled']}; - }} - - QLineEdit, - QTextEdit, - QPlainTextEdit, - QComboBox, - QSpinBox, - QDoubleSpinBox, - QDateEdit, - QDateTimeEdit, - QTimeEdit {{ - min-height: 28px; - padding: 3px 8px; - border: 1px solid {palette['border']}; - border-radius: 6px; - background-color: {palette['input']}; - color: {palette['text']}; - selection-background-color: {palette['selection']}; - selection-color: {palette['selection_text']}; - }} - - QLineEdit:hover, - QTextEdit:hover, - QPlainTextEdit:hover, - QComboBox:hover, - QSpinBox:hover, - QDoubleSpinBox:hover, - QDateEdit:hover, - QDateTimeEdit:hover, - QTimeEdit:hover {{ - border-color: {palette['border_strong']}; - }} - - QLineEdit:focus, - QTextEdit:focus, - QPlainTextEdit:focus, - QComboBox:focus, - QSpinBox:focus, - QDoubleSpinBox:focus, - QDateEdit:focus, - QDateTimeEdit:focus, - QTimeEdit:focus {{ - border-color: {palette['accent']}; - background-color: {palette['input_focus']}; - }} - - QLineEdit:read-only, - QTextEdit:read-only, - QPlainTextEdit:read-only {{ - background-color: {palette['panel_alt']}; - color: {palette['muted']}; - }} - - QLineEdit:disabled, - QTextEdit:disabled, - QPlainTextEdit:disabled, - QComboBox:disabled, - QSpinBox:disabled, - QDoubleSpinBox:disabled, - QDateEdit:disabled, - QDateTimeEdit:disabled, - QTimeEdit:disabled {{ - border-color: {palette['border']}; - background-color: {palette['raised']}; - color: {palette['disabled']}; - }} - - QComboBox {{ - padding-right: 32px; - }} - - QComboBox::drop-down {{ - subcontrol-origin: border; - subcontrol-position: top right; - width: 27px; - border: none; - border-left: 1px solid {palette['border']}; - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; - background-color: transparent; - }} - - QComboBox::drop-down:hover {{ - background-color: {palette['hover']}; - }} - - QComboBox::down-arrow {{ - width: 9px; - height: 9px; - image: url("{caretDownIcon}"); - }} - - QComboBox QAbstractItemView {{ - padding: 4px; - border: 1px solid {palette['border_strong']}; - border-radius: 6px; - background-color: {palette['overlay']}; - color: {palette['text']}; - selection-background-color: {palette['selection']}; - selection-color: {palette['selection_text']}; - }} - - QSpinBox, - QDoubleSpinBox {{ - padding-right: 31px; - }} - - QSpinBox::up-button, - QDoubleSpinBox::up-button {{ - subcontrol-origin: border; - subcontrol-position: top right; - width: 28px; - height: 16px; - border: none; - border-left: 1px solid {palette['border']}; - border-bottom: 1px solid {palette['border']}; - border-top-right-radius: 6px; - background-color: transparent; - }} - - QSpinBox::down-button, - QDoubleSpinBox::down-button {{ - subcontrol-origin: border; - subcontrol-position: bottom right; - width: 28px; - height: 16px; - border: none; - border-left: 1px solid {palette['border']}; - border-top: 1px solid {palette['border']}; - border-bottom-right-radius: 6px; - background-color: transparent; - }} - - QSpinBox::up-button:hover, - QSpinBox::down-button:hover, - QDoubleSpinBox::up-button:hover, - QDoubleSpinBox::down-button:hover {{ - background-color: {palette['hover']}; - }} - - QSpinBox::up-arrow, - QDoubleSpinBox::up-arrow {{ - width: 8px; - height: 8px; - image: url("{caretUpIcon}"); - }} - - QSpinBox::down-arrow, - QDoubleSpinBox::down-arrow {{ - width: 8px; - height: 8px; - image: url("{caretDownIcon}"); - }} - - QTabWidget::pane {{ - top: -1px; - border: 1px solid {palette['border']}; - border-radius: 8px; - background-color: {palette['panel']}; - }} - - QTabBar {{ - background-color: transparent; - }} - - QTabBar::tab {{ - min-height: 28px; - padding: 6px 14px; - margin-right: 2px; - border: none; - border-bottom: 2px solid transparent; - background-color: transparent; - color: {palette['muted']}; - }} - - QTabBar::tab:selected {{ - border-bottom-color: {palette['accent']}; - background-color: {palette['panel']}; - color: {palette['text_strong']}; - font-weight: 600; - }} - - QTabBar::tab:hover:!selected {{ - background-color: {palette['hover']}; - color: {palette['text']}; - }} - - QTabBar::tab:disabled {{ - color: {palette['disabled']}; - }} - - QTableView, - QTableWidget, - QTreeView, - QListView, - QListWidget {{ - border: 1px solid {palette['border']}; - border-radius: 8px; - background-color: {palette['panel']}; - alternate-background-color: {palette['panel_alt']}; - gridline-color: {palette['border']}; - color: {palette['text']}; - selection-background-color: {palette['selection']}; - selection-color: {palette['selection_text']}; - }} - - QTableView::item, - QTableWidget::item, - QTreeView::item, - QListView::item, - QListWidget::item {{ - min-height: 24px; - padding: 4px 7px; - border: none; - }} - - QTableView::item:hover, - QTableWidget::item:hover, - QTreeView::item:hover, - QListView::item:hover, - QListWidget::item:hover {{ - background-color: {palette['hover']}; - color: {palette['text_strong']}; - }} - - QTableView::item:selected, - QTableWidget::item:selected, - QTreeView::item:selected, - QListView::item:selected, - QListWidget::item:selected {{ - background-color: {palette['selection']}; - color: {palette['selection_text']}; - }} - - QTableView::item:selected:!active, - QTableWidget::item:selected:!active, - QTreeView::item:selected:!active, - QListView::item:selected:!active, - QListWidget::item:selected:!active {{ - background-color: {palette['raised']}; - color: {palette['text']}; - }} - - QHeaderView {{ - border: none; - background-color: {palette['panel_alt']}; - }} - - QHeaderView::section {{ - min-height: 28px; - padding: 5px 8px; - border: none; - border-right: 1px solid {palette['border']}; - border-bottom: 1px solid {palette['border']}; - background-color: {palette['raised']}; - color: {palette['muted']}; - font-weight: 600; - }} - - QHeaderView::section:hover {{ - background-color: {palette['hover']}; - color: {palette['text_strong']}; - }} - - QHeaderView::section:pressed {{ - background-color: {palette['pressed']}; - }} - - QAbstractScrollArea::corner {{ - border: none; - background-color: {palette['panel_alt']}; - }} - - QScrollBar:vertical {{ - width: 11px; - margin: 0; - border: none; - background-color: transparent; - }} - - QScrollBar:horizontal {{ - height: 11px; - margin: 0; - border: none; - background-color: transparent; - }} - - QScrollBar::handle:vertical, - QScrollBar::handle:horizontal {{ - min-height: 28px; - min-width: 28px; - margin: 2px; - border-radius: 3px; - background-color: {palette['scroll_handle']}; - }} - - QScrollBar::handle:vertical:hover, - QScrollBar::handle:horizontal:hover {{ - background-color: {palette['scroll_handle_hover']}; - }} - - QScrollBar::add-line, - QScrollBar::sub-line, - QScrollBar::add-page, - QScrollBar::sub-page {{ - width: 0; - height: 0; - border: none; - background-color: transparent; - }} - - {progressBarStyleSheet} - - QCheckBox, - QRadioButton {{ - spacing: 7px; - background-color: transparent; - color: {palette['text']}; - }} - - QCheckBox:hover, - QRadioButton:hover {{ - color: {palette['text_strong']}; - }} - - QCheckBox:disabled, - QRadioButton:disabled {{ - color: {palette['disabled']}; - }} - - QCheckBox::indicator {{ - width: 15px; - height: 15px; - border: 1px solid {palette['border_strong']}; - border-radius: 4px; - background-color: {palette['input']}; - }} - - QCheckBox::indicator:hover {{ - border-color: {palette['accent']}; - background-color: {palette['accent_soft']}; - }} - - QCheckBox::indicator:checked {{ - border-color: {palette['accent']}; - background-color: {palette['accent']}; - image: url("{checkIcon}"); - }} - - QCheckBox::indicator:indeterminate {{ - border: 4px solid {palette['accent']}; - background-color: {palette['input']}; - }} - - QRadioButton::indicator {{ - width: 15px; - height: 15px; - border: 1px solid {palette['border_strong']}; - border-radius: 8px; - background-color: {palette['input']}; - }} - - QRadioButton::indicator:hover {{ - border-color: {palette['accent']}; - background-color: {palette['accent_soft']}; - }} - - QRadioButton::indicator:checked {{ - border: 5px solid {palette['accent']}; - background-color: {palette['input']}; - }} - - QCheckBox::indicator:disabled, - QRadioButton::indicator:disabled {{ - border-color: {palette['border']}; - background-color: {palette['raised']}; - }} - - QSlider::groove:horizontal {{ - height: 4px; - border-radius: 2px; - background-color: {palette['raised']}; - }} - - QSlider::sub-page:horizontal {{ - border-radius: 2px; - background-color: {palette['accent']}; - }} - - QSlider::handle:horizontal {{ - width: 14px; - height: 14px; - margin: -6px 0; - border: 2px solid {palette['panel']}; - border-radius: 8px; - background-color: {palette['accent']}; - }} - - QSlider::handle:horizontal:hover {{ - background-color: {palette['accent_hover']}; - }} - - QStatusBar {{ - min-height: 30px; - border-top: 1px solid {palette['border']}; - background-color: {palette['panel']}; - color: {palette['muted']}; - }} - - QStatusBar::item {{ - border: none; - }} - - QFrame#NetworkStateBadge {{ - min-height: 24px; - border: 1px solid {palette['border']}; - border-radius: 6px; - background-color: {palette['raised']}; - color: {palette['muted']}; - }} - - QFrame#NetworkStateBadge QLabel {{ - border: none; - padding: 0; - background-color: transparent; - color: {palette['muted']}; - }} - - QFrame#NetworkStateBadge QLabel#NetworkStateLabel {{ - color: {palette['text_strong']}; - font-weight: 600; - }} - - QFrame#NetworkStateBadge[networkState="disconnected"] QLabel#NetworkStateLabel {{ - color: {palette['muted']}; - }} - - QFrame#NetworkStateBadge[networkState="connecting"] {{ - border-color: {palette['accent']}; - background-color: {palette['accent_soft']}; - }} - - QFrame#NetworkStateBadge[networkState="connecting"] QLabel#NetworkStateLabel {{ - color: {palette['accent']}; - }} - - QFrame#NetworkStateBadge[networkState="connected"], - QFrame#NetworkStateBadge[networkState="success"] {{ - border-color: {palette['success']}; - background-color: {palette['success_soft']}; - }} - - QFrame#NetworkStateBadge[networkState="connected"] QLabel#NetworkStateLabel, - QFrame#NetworkStateBadge[networkState="success"] QLabel#NetworkStateLabel {{ - color: {palette['success']}; - }} - - QFrame#NetworkStateBadge[networkState="disconnecting"] {{ - border-color: {palette['warning']}; - background-color: {palette['warning_soft']}; - }} - - QFrame#NetworkStateBadge[networkState="disconnecting"] QLabel#NetworkStateLabel {{ - color: {palette['warning']}; - }} - - QFrame#NetworkStateBadge[networkState="failure"] {{ - border-color: {palette['danger']}; - background-color: {palette['danger_soft']}; - }} - - QFrame#NetworkStateBadge[networkState="failure"] QLabel#NetworkStateLabel {{ - color: {palette['danger']}; - }} - - QWidget#TrafficStatsBadge {{ - min-height: 24px; - padding: 0; - border: none; - background-color: transparent; - }} - - QFrame#TrafficDirectionBadge {{ - border: 1px solid {palette['border']}; - border-radius: 6px; - background-color: {palette['raised']}; - color: {palette['text_strong']}; - }} - - QFrame#TrafficDirectionBadge QLabel {{ - border: none; - padding: 0; - background-color: transparent; - color: {palette['text_strong']}; - }} - - QFrame#TrafficDirectionBadge[direction="download"] QLabel#TrafficSpeedLabel {{ - color: {palette['metrics_download']}; - font-weight: 600; - }} - - QFrame#TrafficDirectionBadge[direction="upload"] QLabel#TrafficSpeedLabel {{ - color: {palette['metrics_upload']}; - font-weight: 600; - }} - - QFrame#TrafficDirectionBadge QLabel#TrafficUsageLabel {{ - color: {palette['muted']}; - }} - - QSplitter::handle {{ - background-color: {palette['border']}; - }} - - QSplitter::handle:hover {{ - background-color: {palette['accent']}; - }} - - QSplitter::handle:horizontal {{ - width: 2px; - }} - - QSplitter::handle:vertical {{ - height: 2px; - }} - """).strip() + return composeStyleSheet( + palette, + progressBarStyleSheet, + caretDownIcon, + caretUpIcon, + caretRightIcon, + checkIcon, + ) diff --git a/Furious/Qt/StyleSheets/Base.py b/Furious/Qt/StyleSheets/Base.py new file mode 100644 index 0000000..71a5289 --- /dev/null +++ b/Furious/Qt/StyleSheets/Base.py @@ -0,0 +1,346 @@ +# Copyright (C) 2024–present Loren Eteval & contributors +# +# This file is part of Furious. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Provide global, page, dialog, settings, and metrics QSS.""" + +from textwrap import dedent + + +def baseStyleSheet(palette): + """Return base application and page component styling.""" + return dedent(f""" + * {{ + outline: none; + }} + + QWidget {{ + background-color: {palette['window']}; + color: {palette['text']}; + selection-background-color: {palette['selection']}; + selection-color: {palette['selection_text']}; + }} + + QMainWindow, + QDialog, + QMessageBox {{ + background-color: {palette['window']}; + }} + + QLabel, + QDialogButtonBox, + QSizeGrip {{ + border: none; + background-color: transparent; + }} + + QLabel:disabled {{ + color: {palette['disabled']}; + }} + + QFrame {{ + border-color: {palette['border']}; + }} + + QFrame#NavigationPanel {{ + border: none; + border-right: 1px solid {palette['border']}; + background-color: {palette['panel_alt']}; + }} + + QFrame#NavigationRailPlaceholder {{ + border: none; + background-color: {palette['panel_alt']}; + }} + + QFrame#AppMessageBoxMask {{ + border: none; + background-color: rgba(0, 0, 0, 84); + }} + + QDialog#AppMessageBox {{ + border: none; + background-color: transparent; + }} + + QFrame#AppMessageBoxSurface {{ + border: 1px solid {palette['border_strong']}; + border-radius: 10px; + background-color: {palette['panel']}; + }} + + QFrame#AppMessageBoxContent, + QWidget#AppMessageBoxTextWidget, + QScrollArea#AppMessageBoxTextViewport, + QScrollArea#AppMessageBoxTextViewport > QWidget > QWidget, + QLabel#AppMessageBoxIcon {{ + border: none; + background-color: transparent; + }} + + QLabel#AppMessageBoxTitle {{ + padding: 0; + border: none; + background-color: transparent; + color: {palette['text_strong']}; + }} + + QLabel#AppMessageBoxBody {{ + padding: 0; + border: none; + background-color: transparent; + color: {palette['muted']}; + }} + + QFrame#AppMessageBoxButtonBar {{ + border-top: 1px solid {palette['border']}; + border-left: none; + border-right: none; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + background-color: {palette['panel_alt']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton {{ + padding: 5px 14px; + border: 1px solid {palette['border']}; + border-radius: 6px; + background-color: {palette['input']}; + color: {palette['text']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton:hover {{ + border-color: {palette['border_strong']}; + background-color: {palette['hover']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton:pressed {{ + background-color: {palette['pressed']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="primary"] {{ + border-color: {palette['accent']}; + background-color: {palette['accent']}; + color: {palette['accent_text']}; + font-weight: 600; + }} + + QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="primary"]:hover {{ + border-color: {palette['accent_hover']}; + background-color: {palette['accent_hover']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="primary"]:pressed {{ + border-color: {palette['accent_pressed']}; + background-color: {palette['accent_pressed']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton[messageBoxRole="destructive"] {{ + border-color: {palette['danger']}; + background-color: {palette['danger_soft']}; + color: {palette['danger']}; + }} + + QFrame#AppMessageBoxButtonBar QPushButton:disabled {{ + border-color: {palette['border']}; + background-color: {palette['raised']}; + color: {palette['disabled']}; + }} + + QStackedWidget#ApplicationPageStack {{ + border: none; + background-color: {palette['window']}; + }} + + QScrollArea#MetricsScrollArea, + QWidget#MetricsPageContent, + QScrollArea#SettingsScrollArea, + QWidget#SettingsPageContent, + QScrollArea#SubscriptionScrollArea, + QWidget#SubscriptionPageContent, + QWidget#HomePageContent, + QWidget#LogPageContent {{ + border: none; + background-color: {palette['window']}; + }} + + QLabel#MetricsPageTitle, + QLabel#LogPageTitle, + QLabel#SubscriptionPageTitle, + QLabel#HomePageTitle, + QLabel#SettingsPageTitle {{ + color: {palette['text_strong']}; + font-size: 16pt; + font-weight: 600; + }} + + QLabel#SettingsSectionTitle {{ + padding: 4px 2px; + color: {palette['text_strong']}; + font-size: 12pt; + font-weight: 600; + }} + + QFrame#SettingsCard {{ + border: 1px solid {palette['border']}; + border-radius: 9px; + background-color: {palette['panel']}; + }} + + QFrame#SettingsCard:hover {{ + border-color: {palette['border_strong']}; + background-color: {palette['panel_alt']}; + }} + + QFrame#SubscriptionEditorForm {{ + border: 1px solid {palette['border']}; + border-radius: 9px; + background-color: {palette['panel']}; + }} + + QLabel#SettingsCardTitle {{ + color: {palette['text_strong']}; + font-weight: 600; + }} + + QLabel#SettingsCardDescription {{ + color: {palette['muted']}; + }} + + QPushButton#SettingsActionButton {{ + min-width: 82px; + }} + + QPushButton#SettingsLinkButton {{ + min-height: 20px; + padding: 0; + border: none; + background-color: transparent; + color: {palette['accent']}; + }} + + QPushButton#SettingsLinkButton:hover {{ + color: {palette['text_strong']}; + }} + + QCheckBox#SettingsToggle::indicator {{ + width: 34px; + height: 18px; + border: 1px solid {palette['border_strong']}; + border-radius: 9px; + background-color: {palette['raised']}; + image: none; + }} + + QCheckBox#SettingsToggle::indicator:hover {{ + border-color: {palette['accent']}; + background-color: {palette['hover']}; + }} + + QCheckBox#SettingsToggle::indicator:checked {{ + border-color: {palette['accent']}; + background-color: {palette['accent']}; + image: none; + }} + + QCheckBox#SettingsToggle::indicator:disabled {{ + border-color: {palette['border']}; + background-color: {palette['raised']}; + image: none; + }} + + QFrame#MetricsSection {{ + border: 1px solid {palette['border']}; + border-radius: 10px; + background-color: {palette['panel']}; + }} + + QLabel#MetricsSectionTitle {{ + color: {palette['text_strong']}; + font-size: 12pt; + font-weight: 600; + }} + + QFrame#MetricCard {{ + border: 1px solid {palette['border']}; + border-radius: 8px; + background-color: {palette['panel_alt']}; + }} + + QLabel#MetricCardTitle {{ + color: {palette['muted']}; + font-weight: 600; + }} + + QLabel#EndpointStatusLabel {{ + color: {palette['text_strong']}; + font-weight: 600; + }} + + QWidget#EndpointStatusWidget {{ + background-color: transparent; + }} + + QLabel#EndpointFieldName {{ + color: {palette['muted']}; + }} + + QLabel#EndpointFieldValue {{ + color: {palette['text_strong']}; + }} + + QWidget#EndpointFieldValueContainer {{ + background-color: transparent; + }} + + QLabel#EndpointNoteLabel {{ + color: {palette['muted']}; + }} + + QPushButton#EndpointCopyButton {{ + min-width: 34px; + padding: 0; + }} + + QWidget#EndpointMapWidget {{ + border: none; + background-color: transparent; + }} + + QWidget#MetricsGraphWidget {{ + border: none; + background-color: transparent; + }} + + QGroupBox {{ + margin-top: 16px; + padding: 14px 12px 12px 12px; + border: 1px solid {palette['border']}; + border-radius: 8px; + background-color: {palette['panel']}; + }} + + QGroupBox::title {{ + subcontrol-origin: margin; + subcontrol-position: top left; + left: 10px; + padding: 0 6px; + background-color: {palette['panel']}; + color: {palette['muted']}; + font-weight: 600; + }} + + """).strip() diff --git a/Furious/Qt/StyleSheets/Controls.py b/Furious/Qt/StyleSheets/Controls.py new file mode 100644 index 0000000..dd1275a --- /dev/null +++ b/Furious/Qt/StyleSheets/Controls.py @@ -0,0 +1,517 @@ +# Copyright (C) 2024–present Loren Eteval & contributors +# +# This file is part of Furious. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Provide menu, navigation, input, and common-control QSS.""" + +from textwrap import dedent + + +def controlStyleSheet( + palette, + caretDownIcon, + caretUpIcon, + caretRightIcon, + checkIcon, +): + """Return interactive control styling.""" + return dedent(f""" + QToolTip {{ + padding: 7px 10px; + border: 1px solid {palette['border_strong']}; + border-radius: 6px; + background-color: {palette['overlay']}; + color: {palette['text_strong']}; + }} + + QMenuBar {{ + padding: 2px 5px; + border: none; + border-bottom: 1px solid {palette['border']}; + background-color: {palette['panel']}; + spacing: 2px; + }} + + QMenuBar::item {{ + min-height: 22px; + padding: 6px 10px; + border-radius: 5px; + background-color: transparent; + }} + + QMenuBar::item:selected {{ + background-color: {palette['hover']}; + color: {palette['text_strong']}; + }} + + QMenuBar::item:pressed {{ + background-color: {palette['pressed']}; + }} + + QMenu {{ + padding: 6px; + border: 1px solid {palette['border_strong']}; + border-radius: 9px; + background-color: {palette['overlay']}; + }} + + QMenu::separator {{ + height: 1px; + margin: 6px 9px; + background-color: {palette['border_strong']}; + }} + + QMenu::item {{ + min-width: 160px; + min-height: 22px; + padding: 7px 32px; + border-radius: 6px; + background-color: transparent; + }} + + QMenu::item:selected {{ + background-color: {palette['accent_soft_hover']}; + color: {palette['text_strong']}; + }} + + QMenu::item:pressed {{ + background-color: {palette['pressed']}; + color: {palette['text_strong']}; + }} + + QMenu::item:disabled {{ + background-color: transparent; + color: {palette['disabled']}; + }} + + QMenu::item:disabled:selected {{ + background-color: transparent; + }} + + QMenu::icon {{ + position: relative; + left: 8px; + }} + + QMenu::indicator {{ + width: 14px; + height: 14px; + position: relative; + left: 8px; + }} + + QMenu::indicator:checked {{ + border-radius: 4px; + background-color: {palette['accent']}; + image: url("{checkIcon}"); + }} + + QMenu::right-arrow {{ + width: 9px; + height: 9px; + position: relative; + right: 8px; + image: url("{caretRightIcon}"); + }} + + QMenu[menuRole="tray"] {{ + padding: 7px; + border-radius: 10px; + }} + + QMenu[menuRole="tray"]::item {{ + min-width: 176px; + min-height: 24px; + padding: 8px 34px; + }} + + QMenu[menuRole="tray"]::separator {{ + margin: 6px 10px; + }} + + QMenu[menuRole="tray"]::icon, + QMenu[menuRole="tray"]::indicator {{ + left: 9px; + }} + + QMenu[menuRole="tray"]::right-arrow {{ + right: 9px; + }} + + QToolBar {{ + padding: 4px 6px; + border: none; + border-bottom: 1px solid {palette['border']}; + background-color: {palette['panel']}; + spacing: 3px; + }} + + QToolBar::separator {{ + width: 1px; + margin: 5px 5px; + background-color: {palette['border']}; + }} + + QToolBar#AppMainWindow_AppQToolBar, + QToolBar#HomePageToolBar {{ + padding: 6px 8px; + spacing: 2px; + background-color: {palette['panel']}; + }} + + QToolBar#AppMainWindow_AppQToolBar::separator, + QToolBar#HomePageToolBar::separator {{ + margin: 8px 4px; + }} + + QToolButton {{ + min-height: 24px; + padding: 5px 8px; + border: 1px solid transparent; + border-radius: 6px; + background-color: transparent; + color: {palette['text']}; + }} + + QToolBar#AppMainWindow_AppQToolBar QToolButton, + QToolBar#HomePageToolBar QToolButton {{ + min-width: 72px; + min-height: 54px; + padding: 5px 11px; + border-radius: 8px; + }} + + QPushButton#NavigationToggleButton, + QPushButton#NavigationPageButton {{ + min-width: 0; + min-height: 38px; + padding: 0; + border: 1px solid transparent; + border-radius: 7px; + background-color: transparent; + color: {palette['text']}; + text-align: left; + }} + + QPushButton#NavigationToggleButton:hover, + QPushButton#NavigationPageButton:hover {{ + border-color: transparent; + background-color: {palette['hover']}; + color: {palette['text_strong']}; + }} + + QPushButton#NavigationPageButton:checked {{ + border: none; + border-radius: 7px; + background-color: {palette['accent_soft']}; + color: {palette['text_strong']}; + font-weight: 600; + }} + + QFrame#NavigationSelectionIndicator {{ + border: none; + border-radius: 0; + background-color: {palette['accent']}; + }} + + QPushButton#NavigationPageButton:checked:hover {{ + background-color: {palette['accent_soft_hover']}; + }} + + QPushButton#NavigationPageButton QLabel {{ + border: none; + background-color: transparent; + color: {palette['text']}; + }} + + QPushButton#NavigationPageButton:checked QLabel {{ + color: {palette['text_strong']}; + font-weight: 600; + }} + + QPushButton#SearchButton {{ + padding: 0; + }} + + QToolButton:hover {{ + border-color: {palette['border']}; + background-color: {palette['hover']}; + color: {palette['text_strong']}; + }} + + QToolButton:pressed {{ + border-color: {palette['border_strong']}; + background-color: {palette['pressed']}; + }} + + QToolButton:checked {{ + border-color: {palette['accent']}; + background-color: {palette['accent_soft']}; + color: {palette['text_strong']}; + }} + + QToolButton:checked:hover {{ + background-color: {palette['accent_soft_hover']}; + }} + + QToolButton:disabled {{ + border-color: transparent; + background-color: transparent; + color: {palette['disabled']}; + }} + + QPushButton {{ + min-height: 28px; + padding: 3px 13px; + border: 1px solid {palette['border_strong']}; + border-radius: 6px; + background-color: {palette['panel']}; + color: {palette['text']}; + }} + + QPushButton:hover {{ + border-color: {palette['accent']}; + background-color: {palette['hover']}; + color: {palette['text_strong']}; + }} + + QPushButton:focus {{ + border: 1px solid {palette['accent']}; + }} + + QPushButton:pressed {{ + border-color: {palette['accent_pressed']}; + background-color: {palette['pressed']}; + }} + + QPushButton:default {{ + border-color: {palette['accent']}; + background-color: {palette['accent']}; + color: {palette['accent_text']}; + font-weight: 600; + }} + + QPushButton:default:hover {{ + border-color: {palette['accent_hover']}; + background-color: {palette['accent_hover']}; + }} + + QPushButton:default:pressed {{ + border-color: {palette['accent_pressed']}; + background-color: {palette['accent_pressed']}; + }} + + QPushButton:flat {{ + border-color: transparent; + background-color: transparent; + }} + + QPushButton:disabled {{ + border-color: {palette['border']}; + background-color: {palette['raised']}; + color: {palette['disabled']}; + }} + + QLineEdit, + QTextEdit, + QPlainTextEdit, + QComboBox, + QSpinBox, + QDoubleSpinBox, + QDateEdit, + QDateTimeEdit, + QTimeEdit {{ + min-height: 28px; + padding: 3px 8px; + border: 1px solid {palette['border']}; + border-radius: 6px; + background-color: {palette['input']}; + color: {palette['text']}; + selection-background-color: {palette['selection']}; + selection-color: {palette['selection_text']}; + }} + + QLineEdit:hover, + QTextEdit:hover, + QPlainTextEdit:hover, + QComboBox:hover, + QSpinBox:hover, + QDoubleSpinBox:hover, + QDateEdit:hover, + QDateTimeEdit:hover, + QTimeEdit:hover {{ + border-color: {palette['border_strong']}; + }} + + QLineEdit:focus, + QTextEdit:focus, + QPlainTextEdit:focus, + QComboBox:focus, + QSpinBox:focus, + QDoubleSpinBox:focus, + QDateEdit:focus, + QDateTimeEdit:focus, + QTimeEdit:focus {{ + border-color: {palette['accent']}; + background-color: {palette['input_focus']}; + }} + + QLineEdit:read-only, + QTextEdit:read-only, + QPlainTextEdit:read-only {{ + background-color: {palette['panel_alt']}; + color: {palette['muted']}; + }} + + QLineEdit:disabled, + QTextEdit:disabled, + QPlainTextEdit:disabled, + QComboBox:disabled, + QSpinBox:disabled, + QDoubleSpinBox:disabled, + QDateEdit:disabled, + QDateTimeEdit:disabled, + QTimeEdit:disabled {{ + border-color: {palette['border']}; + background-color: {palette['raised']}; + color: {palette['disabled']}; + }} + + QComboBox {{ + padding-right: 32px; + }} + + QComboBox::drop-down {{ + subcontrol-origin: border; + subcontrol-position: top right; + width: 27px; + border: none; + border-left: 1px solid {palette['border']}; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + background-color: transparent; + }} + + QComboBox::drop-down:hover {{ + background-color: {palette['hover']}; + }} + + QComboBox::down-arrow {{ + width: 9px; + height: 9px; + image: url("{caretDownIcon}"); + }} + + QComboBox QAbstractItemView {{ + padding: 4px; + border: 1px solid {palette['border_strong']}; + border-radius: 6px; + background-color: {palette['overlay']}; + color: {palette['text']}; + selection-background-color: {palette['selection']}; + selection-color: {palette['selection_text']}; + }} + + QSpinBox, + QDoubleSpinBox {{ + padding-right: 31px; + }} + + QSpinBox::up-button, + QDoubleSpinBox::up-button {{ + subcontrol-origin: border; + subcontrol-position: top right; + width: 28px; + height: 16px; + border: none; + border-left: 1px solid {palette['border']}; + border-bottom: 1px solid {palette['border']}; + border-top-right-radius: 6px; + background-color: transparent; + }} + + QSpinBox::down-button, + QDoubleSpinBox::down-button {{ + subcontrol-origin: border; + subcontrol-position: bottom right; + width: 28px; + height: 16px; + border: none; + border-left: 1px solid {palette['border']}; + border-top: 1px solid {palette['border']}; + border-bottom-right-radius: 6px; + background-color: transparent; + }} + + QSpinBox::up-button:hover, + QSpinBox::down-button:hover, + QDoubleSpinBox::up-button:hover, + QDoubleSpinBox::down-button:hover {{ + background-color: {palette['hover']}; + }} + + QSpinBox::up-arrow, + QDoubleSpinBox::up-arrow {{ + width: 8px; + height: 8px; + image: url("{caretUpIcon}"); + }} + + QSpinBox::down-arrow, + QDoubleSpinBox::down-arrow {{ + width: 8px; + height: 8px; + image: url("{caretDownIcon}"); + }} + + QTabWidget::pane {{ + top: -1px; + border: 1px solid {palette['border']}; + border-radius: 8px; + background-color: {palette['panel']}; + }} + + QTabBar {{ + background-color: transparent; + }} + + QTabBar::tab {{ + min-height: 28px; + padding: 6px 14px; + margin-right: 2px; + border: none; + border-bottom: 2px solid transparent; + background-color: transparent; + color: {palette['muted']}; + }} + + QTabBar::tab:selected {{ + border-bottom-color: {palette['accent']}; + background-color: {palette['panel']}; + color: {palette['text_strong']}; + font-weight: 600; + }} + + QTabBar::tab:hover:!selected {{ + background-color: {palette['hover']}; + color: {palette['text']}; + }} + + QTabBar::tab:disabled {{ + color: {palette['disabled']}; + }} + + """).strip() diff --git a/Furious/Qt/StyleSheets/DataViews.py b/Furious/Qt/StyleSheets/DataViews.py new file mode 100644 index 0000000..2078431 --- /dev/null +++ b/Furious/Qt/StyleSheets/DataViews.py @@ -0,0 +1,360 @@ +# Copyright (C) 2024–present Loren Eteval & contributors +# +# This file is part of Furious. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Provide item-view, scrolling, selection, and status QSS.""" + +from textwrap import dedent + + +def dataViewStyleSheet(palette, progressBarStyleSheet, checkIcon): + """Return data-view and status component styling.""" + return dedent(f""" + QTableView, + QTableWidget, + QTreeView, + QListView, + QListWidget {{ + border: 1px solid {palette['border']}; + border-radius: 8px; + background-color: {palette['panel']}; + alternate-background-color: {palette['panel_alt']}; + gridline-color: {palette['border']}; + color: {palette['text']}; + selection-background-color: {palette['selection']}; + selection-color: {palette['selection_text']}; + }} + + QTableView::item, + QTableWidget::item, + QTreeView::item, + QListView::item, + QListWidget::item {{ + min-height: 24px; + padding: 4px 7px; + border: none; + }} + + QTableView::item:hover, + QTableWidget::item:hover, + QTreeView::item:hover, + QListView::item:hover, + QListWidget::item:hover {{ + background-color: {palette['hover']}; + color: {palette['text_strong']}; + }} + + QTableView::item:selected, + QTableWidget::item:selected, + QTreeView::item:selected, + QListView::item:selected, + QListWidget::item:selected {{ + background-color: {palette['selection']}; + color: {palette['selection_text']}; + }} + + QTableView::item:selected:!active, + QTableWidget::item:selected:!active, + QTreeView::item:selected:!active, + QListView::item:selected:!active, + QListWidget::item:selected:!active {{ + background-color: {palette['raised']}; + color: {palette['text']}; + }} + + QHeaderView {{ + border: none; + background-color: {palette['panel_alt']}; + }} + + QHeaderView::section {{ + min-height: 28px; + padding: 5px 8px; + border: none; + border-right: 1px solid {palette['border']}; + border-bottom: 1px solid {palette['border']}; + background-color: {palette['raised']}; + color: {palette['muted']}; + font-weight: 600; + }} + + QHeaderView::section:hover {{ + background-color: {palette['hover']}; + color: {palette['text_strong']}; + }} + + QHeaderView::section:pressed {{ + background-color: {palette['pressed']}; + }} + + QAbstractScrollArea::corner {{ + border: none; + background-color: {palette['panel_alt']}; + }} + + QScrollBar:vertical {{ + width: 11px; + margin: 0; + border: none; + background-color: transparent; + }} + + QScrollBar:horizontal {{ + height: 11px; + margin: 0; + border: none; + background-color: transparent; + }} + + QScrollBar::handle:vertical, + QScrollBar::handle:horizontal {{ + min-height: 28px; + min-width: 28px; + margin: 2px; + border-radius: 3px; + background-color: {palette['scroll_handle']}; + }} + + QScrollBar::handle:vertical:hover, + QScrollBar::handle:horizontal:hover {{ + background-color: {palette['scroll_handle_hover']}; + }} + + QScrollBar::add-line, + QScrollBar::sub-line, + QScrollBar::add-page, + QScrollBar::sub-page {{ + width: 0; + height: 0; + border: none; + background-color: transparent; + }} + + {progressBarStyleSheet} + + QCheckBox, + QRadioButton {{ + spacing: 7px; + background-color: transparent; + color: {palette['text']}; + }} + + QCheckBox:hover, + QRadioButton:hover {{ + color: {palette['text_strong']}; + }} + + QCheckBox:disabled, + QRadioButton:disabled {{ + color: {palette['disabled']}; + }} + + QCheckBox::indicator {{ + width: 15px; + height: 15px; + border: 1px solid {palette['border_strong']}; + border-radius: 4px; + background-color: {palette['input']}; + }} + + QCheckBox::indicator:hover {{ + border-color: {palette['accent']}; + background-color: {palette['accent_soft']}; + }} + + QCheckBox::indicator:checked {{ + border-color: {palette['accent']}; + background-color: {palette['accent']}; + image: url("{checkIcon}"); + }} + + QCheckBox::indicator:indeterminate {{ + border: 4px solid {palette['accent']}; + background-color: {palette['input']}; + }} + + QRadioButton::indicator {{ + width: 15px; + height: 15px; + border: 1px solid {palette['border_strong']}; + border-radius: 8px; + background-color: {palette['input']}; + }} + + QRadioButton::indicator:hover {{ + border-color: {palette['accent']}; + background-color: {palette['accent_soft']}; + }} + + QRadioButton::indicator:checked {{ + border: 5px solid {palette['accent']}; + background-color: {palette['input']}; + }} + + QCheckBox::indicator:disabled, + QRadioButton::indicator:disabled {{ + border-color: {palette['border']}; + background-color: {palette['raised']}; + }} + + QSlider::groove:horizontal {{ + height: 4px; + border-radius: 2px; + background-color: {palette['raised']}; + }} + + QSlider::sub-page:horizontal {{ + border-radius: 2px; + background-color: {palette['accent']}; + }} + + QSlider::handle:horizontal {{ + width: 14px; + height: 14px; + margin: -6px 0; + border: 2px solid {palette['panel']}; + border-radius: 8px; + background-color: {palette['accent']}; + }} + + QSlider::handle:horizontal:hover {{ + background-color: {palette['accent_hover']}; + }} + + QStatusBar {{ + min-height: 30px; + border-top: 1px solid {palette['border']}; + background-color: {palette['panel']}; + color: {palette['muted']}; + }} + + QStatusBar::item {{ + border: none; + }} + + QFrame#NetworkStateBadge {{ + min-height: 24px; + border: 1px solid {palette['border']}; + border-radius: 6px; + background-color: {palette['raised']}; + color: {palette['muted']}; + }} + + QFrame#NetworkStateBadge QLabel {{ + border: none; + padding: 0; + background-color: transparent; + color: {palette['muted']}; + }} + + QFrame#NetworkStateBadge QLabel#NetworkStateLabel {{ + color: {palette['text_strong']}; + font-weight: 600; + }} + + QFrame#NetworkStateBadge[networkState="disconnected"] QLabel#NetworkStateLabel {{ + color: {palette['muted']}; + }} + + QFrame#NetworkStateBadge[networkState="connecting"] {{ + border-color: {palette['accent']}; + background-color: {palette['accent_soft']}; + }} + + QFrame#NetworkStateBadge[networkState="connecting"] QLabel#NetworkStateLabel {{ + color: {palette['accent']}; + }} + + QFrame#NetworkStateBadge[networkState="connected"], + QFrame#NetworkStateBadge[networkState="success"] {{ + border-color: {palette['success']}; + background-color: {palette['success_soft']}; + }} + + QFrame#NetworkStateBadge[networkState="connected"] QLabel#NetworkStateLabel, + QFrame#NetworkStateBadge[networkState="success"] QLabel#NetworkStateLabel {{ + color: {palette['success']}; + }} + + QFrame#NetworkStateBadge[networkState="disconnecting"] {{ + border-color: {palette['warning']}; + background-color: {palette['warning_soft']}; + }} + + QFrame#NetworkStateBadge[networkState="disconnecting"] QLabel#NetworkStateLabel {{ + color: {palette['warning']}; + }} + + QFrame#NetworkStateBadge[networkState="failure"] {{ + border-color: {palette['danger']}; + background-color: {palette['danger_soft']}; + }} + + QFrame#NetworkStateBadge[networkState="failure"] QLabel#NetworkStateLabel {{ + color: {palette['danger']}; + }} + + QWidget#TrafficStatsBadge {{ + min-height: 24px; + padding: 0; + border: none; + background-color: transparent; + }} + + QFrame#TrafficDirectionBadge {{ + border: 1px solid {palette['border']}; + border-radius: 6px; + background-color: {palette['raised']}; + color: {palette['text_strong']}; + }} + + QFrame#TrafficDirectionBadge QLabel {{ + border: none; + padding: 0; + background-color: transparent; + color: {palette['text_strong']}; + }} + + QFrame#TrafficDirectionBadge[direction="download"] QLabel#TrafficSpeedLabel {{ + color: {palette['metrics_download']}; + font-weight: 600; + }} + + QFrame#TrafficDirectionBadge[direction="upload"] QLabel#TrafficSpeedLabel {{ + color: {palette['metrics_upload']}; + font-weight: 600; + }} + + QFrame#TrafficDirectionBadge QLabel#TrafficUsageLabel {{ + color: {palette['muted']}; + }} + + QSplitter::handle {{ + background-color: {palette['border']}; + }} + + QSplitter::handle:hover {{ + background-color: {palette['accent']}; + }} + + QSplitter::handle:horizontal {{ + width: 2px; + }} + + QSplitter::handle:vertical {{ + height: 2px; + }} + """).strip() diff --git a/Furious/Qt/StyleSheets/__init__.py b/Furious/Qt/StyleSheets/__init__.py new file mode 100644 index 0000000..30ac0b3 --- /dev/null +++ b/Furious/Qt/StyleSheets/__init__.py @@ -0,0 +1,49 @@ +# Copyright (C) 2024–present Loren Eteval & contributors +# +# This file is part of Furious. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Compose private component QSS behind :class:`AppStyleSheet`.""" + +from .Base import baseStyleSheet +from .Controls import controlStyleSheet +from .DataViews import dataViewStyleSheet + + +def composeStyleSheet( + palette, + progressBarStyleSheet, + caretDownIcon, + caretUpIcon, + caretRightIcon, + checkIcon, +): + """Return the complete stylesheet without exposing component fragments.""" + return '\n\n'.join( + ( + baseStyleSheet(palette), + controlStyleSheet( + palette, + caretDownIcon, + caretUpIcon, + caretRightIcon, + checkIcon, + ), + dataViewStyleSheet(palette, progressBarStyleSheet, checkIcon), + ) + ) + + +__all__ = []