mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-10-06 05:47:59 +03:00
Unify application theme design system
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
+142
-14
@@ -33,6 +33,7 @@ class AppStyleSheet:
|
||||
|
||||
Palettes = {
|
||||
Light: {
|
||||
# Application surfaces and interaction states.
|
||||
'window': '#F4F7FB',
|
||||
'panel': '#FFFFFF',
|
||||
'panel_alt': '#F8FAFD',
|
||||
@@ -64,8 +65,37 @@ class AppStyleSheet:
|
||||
'warning_soft': '#FFF4D6',
|
||||
'scroll_handle': '#B9C5D4',
|
||||
'scroll_handle_hover': '#93A3B7',
|
||||
# Editor semantics.
|
||||
'editor_background': '#F8FAFC',
|
||||
'editor_text': '#172033',
|
||||
'editor_keyword': '#7C3AED',
|
||||
'editor_string': '#0F766E',
|
||||
'editor_number': '#B45309',
|
||||
'editor_comment': '#64748B',
|
||||
'editor_ip': '#0369A1',
|
||||
'editor_url': '#2563EB',
|
||||
'editor_warning': '#A16207',
|
||||
'editor_error': '#D33F5A',
|
||||
'editor_symbol': '#475569',
|
||||
'editor_key': '#047857',
|
||||
'editor_timestamp': '#64748B',
|
||||
'editor_logger': '#C2410C',
|
||||
'editor_info': '#15803D',
|
||||
'editor_debug': '#2563EB',
|
||||
'editor_critical': '#B91C1C',
|
||||
'editor_selection': '#D8E8FF',
|
||||
'editor_selection_text': '#102A56',
|
||||
# Progress and connection semantics.
|
||||
'progress_background': '#DCE5F0',
|
||||
'progress_chunk_start': '#2563EB',
|
||||
'progress_chunk_end': '#22B8CF',
|
||||
'progress_text': '#172033',
|
||||
'connection_disconnected': '#D94F64',
|
||||
'connection_connecting': '#3B82F6',
|
||||
'connection_connected': '#159A70',
|
||||
},
|
||||
Dark: {
|
||||
# Application surfaces and interaction states.
|
||||
'window': '#0F131A',
|
||||
'panel': '#151A22',
|
||||
'panel_alt': '#11161D',
|
||||
@@ -97,6 +127,34 @@ class AppStyleSheet:
|
||||
'warning_soft': '#352C18',
|
||||
'scroll_handle': '#3B4654',
|
||||
'scroll_handle_hover': '#536171',
|
||||
# Editor semantics.
|
||||
'editor_background': '#10151D',
|
||||
'editor_text': '#E7ECF4',
|
||||
'editor_keyword': '#C4A7FF',
|
||||
'editor_string': '#85E0B7',
|
||||
'editor_number': '#F5B971',
|
||||
'editor_comment': '#8290A4',
|
||||
'editor_ip': '#80D8FF',
|
||||
'editor_url': '#8AB4FF',
|
||||
'editor_warning': '#F7C65F',
|
||||
'editor_error': '#FF7A8A',
|
||||
'editor_symbol': '#C7D0DD',
|
||||
'editor_key': '#6EE7B7',
|
||||
'editor_timestamp': '#8290A4',
|
||||
'editor_logger': '#FFAD7A',
|
||||
'editor_info': '#7DD3A8',
|
||||
'editor_debug': '#82B4FF',
|
||||
'editor_critical': '#FF5C70',
|
||||
'editor_selection': '#214A7D',
|
||||
'editor_selection_text': '#F6F9FF',
|
||||
# Progress and connection semantics.
|
||||
'progress_background': '#242C38',
|
||||
'progress_chunk_start': '#5B9BFF',
|
||||
'progress_chunk_end': '#36D6C5',
|
||||
'progress_text': '#F7F9FC',
|
||||
'connection_disconnected': '#FF6B7A',
|
||||
'connection_connecting': '#5B9BFF',
|
||||
'connection_connected': '#42D39A',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -108,11 +166,93 @@ class AppStyleSheet:
|
||||
|
||||
return AppStyleSheet.Light
|
||||
|
||||
@staticmethod
|
||||
def paletteForTheme(theme):
|
||||
"""Return the semantic token palette for *theme*."""
|
||||
return AppStyleSheet.Palettes[AppStyleSheet.normalizeTheme(theme)]
|
||||
|
||||
@staticmethod
|
||||
def editorStyleSheet(widgetName, fontFamily='', theme=Light):
|
||||
"""Return editor chrome built exclusively from semantic theme tokens."""
|
||||
palette = AppStyleSheet.paletteForTheme(theme)
|
||||
escapedFontFamily = fontFamily.replace('\\', '\\\\').replace("'", "\\'")
|
||||
fontDeclaration = ''
|
||||
|
||||
if escapedFontFamily:
|
||||
fontDeclaration = f"font-family: '{escapedFontFamily}';"
|
||||
|
||||
return dedent(f"""
|
||||
{widgetName} {{
|
||||
border: 1px solid {palette['border']};
|
||||
border-radius: 6px;
|
||||
background-color: {palette['editor_background']};
|
||||
color: {palette['editor_text']};
|
||||
selection-background-color: {palette['editor_selection']};
|
||||
selection-color: {palette['editor_selection_text']};
|
||||
{fontDeclaration}
|
||||
}}
|
||||
|
||||
{widgetName}:hover {{
|
||||
border-color: {palette['border_strong']};
|
||||
}}
|
||||
|
||||
{widgetName}:focus {{
|
||||
border-color: {palette['accent']};
|
||||
background-color: {palette['editor_background']};
|
||||
}}
|
||||
""").strip()
|
||||
|
||||
@staticmethod
|
||||
def progressBarStyleSheet(theme=Light):
|
||||
"""Return progress styling built from progress and connection tokens."""
|
||||
palette = AppStyleSheet.paletteForTheme(theme)
|
||||
|
||||
return dedent(f"""
|
||||
QProgressBar {{
|
||||
min-height: 12px;
|
||||
border: 1px solid {palette['border']};
|
||||
border-radius: 6px;
|
||||
background-color: {palette['progress_background']};
|
||||
color: {palette['progress_text']};
|
||||
text-align: center;
|
||||
}}
|
||||
|
||||
QProgressBar::chunk {{
|
||||
border-radius: 5px;
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0, x2: 1, y2: 0,
|
||||
stop: 0 {palette['progress_chunk_start']},
|
||||
stop: 1 {palette['progress_chunk_end']}
|
||||
);
|
||||
}}
|
||||
|
||||
QProgressBar[connectionState="disconnected"]::chunk {{
|
||||
background: {palette['connection_disconnected']};
|
||||
}}
|
||||
|
||||
QProgressBar[connectionState="connecting"]::chunk {{
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0, x2: 1, y2: 0,
|
||||
stop: 0 {palette['connection_connecting']},
|
||||
stop: 1 {palette['progress_chunk_end']}
|
||||
);
|
||||
}}
|
||||
|
||||
QProgressBar[connectionState="connected"]::chunk {{
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0, x2: 1, y2: 0,
|
||||
stop: 0 {palette['progress_chunk_start']},
|
||||
stop: 1 {palette['connection_connected']}
|
||||
);
|
||||
}}
|
||||
""").strip()
|
||||
|
||||
@staticmethod
|
||||
def forTheme(theme):
|
||||
"""Return the complete Qt style sheet for *theme*."""
|
||||
normalizedTheme = AppStyleSheet.normalizeTheme(theme)
|
||||
palette = AppStyleSheet.Palettes[normalizedTheme]
|
||||
palette = AppStyleSheet.paletteForTheme(normalizedTheme)
|
||||
progressBarStyleSheet = AppStyleSheet.progressBarStyleSheet(normalizedTheme)
|
||||
iconPrefix = ':/Icons/bootstrap'
|
||||
|
||||
if normalizedTheme == AppStyleSheet.Dark:
|
||||
@@ -689,19 +829,7 @@ class AppStyleSheet:
|
||||
background-color: transparent;
|
||||
}}
|
||||
|
||||
QProgressBar {{
|
||||
min-height: 8px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: {palette['raised']};
|
||||
color: {palette['text']};
|
||||
text-align: center;
|
||||
}}
|
||||
|
||||
QProgressBar::chunk {{
|
||||
border-radius: 4px;
|
||||
background-color: {palette['accent']};
|
||||
}}
|
||||
{progressBarStyleSheet}
|
||||
|
||||
QCheckBox,
|
||||
QRadioButton {{
|
||||
|
||||
@@ -287,12 +287,12 @@ class AppQTextBrowser(SupportPointSizeSettings, QTextBrowser):
|
||||
AppSettings.set(self.pointSizeSettingsName, str(self.font().pointSize()))
|
||||
|
||||
|
||||
class DraculaTextEditor(AppQPlainTextEdit):
|
||||
"""Represent dracula text editor."""
|
||||
class DraculaTextEditor(Mixins.ThemeAware, AppQPlainTextEdit):
|
||||
"""Represent the legacy-named, application-themed text editor."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the DraculaTextEditor."""
|
||||
fontFamily = kwargs.pop('fontFamily', '')
|
||||
self._fontFamily = kwargs.pop('fontFamily', '')
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -302,7 +302,7 @@ class DraculaTextEditor(AppQPlainTextEdit):
|
||||
# Theme
|
||||
self.setStyleSheet(
|
||||
DraculaEditorTheme.getStyleSheet(
|
||||
widgetName='QPlainTextEdit', fontFamily=fontFamily
|
||||
widgetName='QPlainTextEdit', fontFamily=self._fontFamily
|
||||
)
|
||||
)
|
||||
|
||||
@@ -324,6 +324,16 @@ class DraculaTextEditor(AppQPlainTextEdit):
|
||||
self.modificationChanged.connect(handleModificationChanged)
|
||||
self.cursorPositionChanged.connect(handleCursorPositionChanged)
|
||||
|
||||
def themeChangedCallback(self, theme: str):
|
||||
"""Refresh editor chrome after an application theme change."""
|
||||
self.setStyleSheet(
|
||||
DraculaEditorTheme.getStyleSheet(
|
||||
widgetName='QPlainTextEdit',
|
||||
fontFamily=self._fontFamily,
|
||||
theme=theme,
|
||||
)
|
||||
)
|
||||
|
||||
def registerCursorPositionChangedCb(self, callback: Callable[[QTextCursor], None]):
|
||||
"""Handle register cursor position changed cb for the dracula text editor."""
|
||||
self._cursorPositionChangedCb = callback
|
||||
@@ -343,12 +353,12 @@ class DraculaJSONTextEditor(DraculaTextEditor):
|
||||
self._syntaxHighlighter = DraculaJSONSyntaxHighlighter(self.document())
|
||||
|
||||
|
||||
class DraculaTextBrowser(AppQTextBrowser):
|
||||
"""Represent dracula text browser."""
|
||||
class DraculaTextBrowser(Mixins.ThemeAware, AppQTextBrowser):
|
||||
"""Represent the legacy-named, application-themed text browser."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the DraculaTextBrowser."""
|
||||
fontFamily = kwargs.pop('fontFamily', '')
|
||||
self._fontFamily = kwargs.pop('fontFamily', '')
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -357,6 +367,16 @@ class DraculaTextBrowser(AppQTextBrowser):
|
||||
# Theme
|
||||
self.setStyleSheet(
|
||||
DraculaEditorTheme.getStyleSheet(
|
||||
widgetName='QTextBrowser', fontFamily=fontFamily
|
||||
widgetName='QTextBrowser', fontFamily=self._fontFamily
|
||||
)
|
||||
)
|
||||
|
||||
def themeChangedCallback(self, theme: str):
|
||||
"""Refresh browser chrome after an application theme change."""
|
||||
self.setStyleSheet(
|
||||
DraculaEditorTheme.getStyleSheet(
|
||||
widgetName='QTextBrowser',
|
||||
fontFamily=self._fontFamily,
|
||||
theme=theme,
|
||||
)
|
||||
)
|
||||
|
||||
+101
-43
@@ -20,10 +20,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Core.Tun2socks import Tun2socks
|
||||
from Furious.Frozenlib.Mixins import Mixins
|
||||
from Furious.Plugins import getPluginRegistry
|
||||
from Furious.Qt.AppStyleSheet import AppStyleSheet
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
__all__ = [
|
||||
'DraculaEditorTheme',
|
||||
@@ -32,6 +35,23 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
def _currentTheme():
|
||||
"""Return the application's active theme without coupling to its concrete type."""
|
||||
app = QApplication.instance()
|
||||
|
||||
if app is not None:
|
||||
themeGetter = getattr(app, 'theme', None)
|
||||
|
||||
if callable(themeGetter):
|
||||
try:
|
||||
return AppStyleSheet.normalizeTheme(themeGetter())
|
||||
except Exception:
|
||||
# A partially initialized application falls back to the safe default.
|
||||
pass
|
||||
|
||||
return AppStyleSheet.Light
|
||||
|
||||
|
||||
class EditorHighlightRules:
|
||||
"""Represent editor highlight rules."""
|
||||
|
||||
@@ -66,33 +86,51 @@ class EditorTheme:
|
||||
|
||||
|
||||
class DraculaEditorTheme(EditorTheme):
|
||||
"""Represent dracula editor theme."""
|
||||
"""Compatibility facade for the application-owned editor theme."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the DraculaEditorTheme."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def getStyleSheet(widgetName, fontFamily):
|
||||
"""Return style sheet."""
|
||||
return (
|
||||
f'{widgetName} {{'
|
||||
f' background-color: #282A36;'
|
||||
f' color: #F8F8F2;'
|
||||
f' font-family: \'{fontFamily}\';'
|
||||
f'}}'
|
||||
def getStyleSheet(widgetName, fontFamily, theme=None):
|
||||
"""Return token-driven editor styling while preserving the legacy API."""
|
||||
if theme is None:
|
||||
theme = _currentTheme()
|
||||
|
||||
return AppStyleSheet.editorStyleSheet(
|
||||
widgetName=widgetName,
|
||||
fontFamily=fontFamily,
|
||||
theme=theme,
|
||||
)
|
||||
|
||||
|
||||
class AppQSyntaxHighlighter(QSyntaxHighlighter):
|
||||
class AppQSyntaxHighlighter(Mixins.ThemeAware, QSyntaxHighlighter):
|
||||
"""Apply syntax highlighting for app q syntax text."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the AppQSyntaxHighlighter."""
|
||||
self._theme = AppStyleSheet.normalizeTheme(kwargs.pop('theme', _currentTheme()))
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.highlightRules = list()
|
||||
|
||||
def buildHighlightRules(self, palette):
|
||||
"""Return highlight rules for *palette*."""
|
||||
return list()
|
||||
|
||||
def applyTheme(self, theme):
|
||||
"""Rebuild formats from semantic tokens and rehighlight the document."""
|
||||
self._theme = AppStyleSheet.normalizeTheme(theme)
|
||||
palette = AppStyleSheet.paletteForTheme(self._theme)
|
||||
self.highlightRules = self.buildHighlightRules(palette)
|
||||
self.rehighlight()
|
||||
|
||||
def themeChangedCallback(self, theme: str):
|
||||
"""Refresh syntax formats after an application theme change."""
|
||||
self.applyTheme(theme)
|
||||
|
||||
def highlightBlock(self, text):
|
||||
"""Handle highlight block for the app q syntax highlighter."""
|
||||
for highlightRule in self.highlightRules:
|
||||
@@ -126,33 +164,38 @@ class AppQSyntaxHighlighter(QSyntaxHighlighter):
|
||||
|
||||
|
||||
class DraculaJSONSyntaxHighlighter(AppQSyntaxHighlighter):
|
||||
"""Apply syntax highlighting for dracula JSON syntax text."""
|
||||
"""Apply application-themed JSON syntax highlighting."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the DraculaJSONSyntaxHighlighter."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.highlightRules = [
|
||||
# Keywords: true, false, null. Pink. Bold
|
||||
EditorHighlightRules(r'\b(true|false|null)\b', '#FF79C6', isBold=True),
|
||||
# Numbers. Purple
|
||||
EditorHighlightRules(r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', '#BD93F9'),
|
||||
# Symbols: :, [, ], {, }. White
|
||||
EditorHighlightRules(r'[:,\[\]\{\}]', '#F8F8F2'),
|
||||
# Double-quoted strings. Yellow
|
||||
EditorHighlightRules(r'"[^"\\]*(\\.[^"\\]*)*"', '#F1FA8C'),
|
||||
# JSON keys. Green
|
||||
self.applyTheme(self._theme)
|
||||
|
||||
def buildHighlightRules(self, palette):
|
||||
"""Return JSON rules resolved from editor semantic tokens."""
|
||||
return [
|
||||
EditorHighlightRules(
|
||||
r'"([^"\\]*(\\.[^"\\]*)*)"\s*:', '#50FA7B', isJSONKey=True
|
||||
r'\b(true|false|null)\b', palette['editor_keyword'], isBold=True
|
||||
),
|
||||
# Comments(only for hints on display). Grey
|
||||
EditorHighlightRules(r'^#.*', '#6272A4'),
|
||||
EditorHighlightRules(
|
||||
r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?',
|
||||
palette['editor_number'],
|
||||
),
|
||||
EditorHighlightRules(r'[:,\[\]\{\}]', palette['editor_symbol']),
|
||||
EditorHighlightRules(r'"[^"\\]*(\\.[^"\\]*)*"', palette['editor_string']),
|
||||
EditorHighlightRules(
|
||||
r'"([^"\\]*(\\.[^"\\]*)*)"\s*:',
|
||||
palette['editor_key'],
|
||||
isJSONKey=True,
|
||||
),
|
||||
EditorHighlightRules(r'^#.*', palette['editor_comment'], isItalic=True),
|
||||
]
|
||||
|
||||
|
||||
class DraculaLoggerSyntaxHighlighter(AppQSyntaxHighlighter):
|
||||
# https://ihateregex.io/expr/ip/
|
||||
"""Apply syntax highlighting for dracula logger syntax text."""
|
||||
"""Apply application-themed network logger syntax highlighting."""
|
||||
IPV4_REGEX = (
|
||||
r'(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)'
|
||||
r'(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}(?::\d{1,5})?\b'
|
||||
@@ -182,17 +225,20 @@ class DraculaLoggerSyntaxHighlighter(AppQSyntaxHighlighter):
|
||||
"""Initialize the DraculaLoggerSyntaxHighlighter."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.highlightRules = [
|
||||
# IP addresses (IPv4 & IPv6)
|
||||
self.applyTheme(self._theme)
|
||||
|
||||
def buildHighlightRules(self, palette):
|
||||
"""Return network-log rules resolved from editor semantic tokens."""
|
||||
return [
|
||||
EditorHighlightRules(
|
||||
DraculaLoggerSyntaxHighlighter.IPV4_REGEX
|
||||
+ r'|'
|
||||
+ DraculaLoggerSyntaxHighlighter.IPV6_REGEX,
|
||||
'#B4FFFF',
|
||||
palette['editor_ip'],
|
||||
),
|
||||
EditorHighlightRules(
|
||||
r'(https?:)?\/\/[^\s,"\'>)\]]+', palette['editor_url']
|
||||
),
|
||||
# URLs
|
||||
EditorHighlightRules(r'(https?:)?\/\/[^\s,"\'>)\]]+', '#FFDD88'),
|
||||
# Timestamps
|
||||
EditorHighlightRules(
|
||||
# Application logging timestamp
|
||||
r'\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}\]' + r'|'
|
||||
@@ -200,23 +246,35 @@ class DraculaLoggerSyntaxHighlighter(AppQSyntaxHighlighter):
|
||||
+ r'|'.join(getPluginRegistry().logTimestampPatterns()) + r'|'
|
||||
# tun2socks logging timestamp
|
||||
+ r'\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}',
|
||||
'#7F7F7F',
|
||||
palette['editor_timestamp'],
|
||||
),
|
||||
# Logger name: [X.Y.Z]
|
||||
EditorHighlightRules(
|
||||
r'\[[A-Za-z0-9_]+\.[A-Za-z0-9_]+\.[A-Za-z0-9_]+\]', '#FFB46E'
|
||||
r'\[[A-Za-z0-9_]+\.[A-Za-z0-9_]+\.[A-Za-z0-9_]+\]',
|
||||
palette['editor_logger'],
|
||||
),
|
||||
# Log levels
|
||||
EditorHighlightRules(r'\[INFO\]|\[Info\]|INFO', '#A0C882', isBold=True),
|
||||
EditorHighlightRules(r'\[DEBUG\]|\[Debug\]|DEBUG', '#82B4FF', isBold=True),
|
||||
EditorHighlightRules(
|
||||
r'\[WARNING\]|\[Warning\]|WARNING', '#FFD75A', isBold=True
|
||||
r'\[INFO\]|\[Info\]|INFO', palette['editor_info'], isBold=True
|
||||
),
|
||||
EditorHighlightRules(r'\[ERROR\]|\[Error\]|ERROR', '#FF7878', isBold=True),
|
||||
EditorHighlightRules(
|
||||
r'\[CRITICAL\]|\[Critical\]|CRITICAL', '#FF5050', isBold=True
|
||||
r'\[DEBUG\]|\[Debug\]|DEBUG',
|
||||
palette['editor_debug'],
|
||||
isBold=True,
|
||||
),
|
||||
# Quoted strings (single or double)
|
||||
EditorHighlightRules(r"'[^']*'", '#A0FFA0'),
|
||||
EditorHighlightRules(r'"[^"]*"', '#A0FFA0'),
|
||||
EditorHighlightRules(
|
||||
r'\[WARNING\]|\[Warning\]|WARNING',
|
||||
palette['editor_warning'],
|
||||
isBold=True,
|
||||
),
|
||||
EditorHighlightRules(
|
||||
r'\[ERROR\]|\[Error\]|ERROR',
|
||||
palette['editor_error'],
|
||||
isBold=True,
|
||||
),
|
||||
EditorHighlightRules(
|
||||
r'\[CRITICAL\]|\[Critical\]|CRITICAL',
|
||||
palette['editor_critical'],
|
||||
isBold=True,
|
||||
),
|
||||
EditorHighlightRules(r"'[^']*'", palette['editor_string']),
|
||||
EditorHighlightRules(r'"[^"]*"', palette['editor_string']),
|
||||
]
|
||||
|
||||
@@ -52,10 +52,25 @@ class AutoUpdateProgressBar(Mixins.ConnectionAware, QProgressBar):
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(update)
|
||||
|
||||
self.setStyleSheet(self.getStyleSheet())
|
||||
self._setConnectionState('disconnected')
|
||||
|
||||
def _setConnectionState(self, state: str):
|
||||
"""Expose semantic state to the application-owned progress style."""
|
||||
if self.property('connectionState') == state:
|
||||
return
|
||||
|
||||
self.setProperty('connectionState', state)
|
||||
|
||||
# Qt does not automatically repolish a widget after a dynamic property
|
||||
# used by a style-sheet selector changes.
|
||||
style = self.style()
|
||||
style.unpolish(self)
|
||||
style.polish(self)
|
||||
self.update()
|
||||
|
||||
def start(self, msec: int):
|
||||
"""Start the auto update progress bar."""
|
||||
self._setConnectionState('connecting')
|
||||
self.timer.start(msec)
|
||||
|
||||
def stop(self):
|
||||
@@ -63,31 +78,23 @@ class AutoUpdateProgressBar(Mixins.ConnectionAware, QProgressBar):
|
||||
self.timer.stop()
|
||||
|
||||
@staticmethod
|
||||
def getStyleSheet():
|
||||
"""Return style sheet."""
|
||||
return (
|
||||
f'QProgressBar {{'
|
||||
f' border-radius: 2px;'
|
||||
f' text-align: center;'
|
||||
f'}}'
|
||||
f''
|
||||
f'QProgressBar::chunk {{'
|
||||
f' background: qlineargradient('
|
||||
f' x1: 0, y1: 0, x2: 1, y2: 0,'
|
||||
f' stop: 0 {AppHue.disconnectedColor()},'
|
||||
f' stop: 1 {AppHue.connectedColor()}'
|
||||
f' );'
|
||||
f' border-radius: 2px;'
|
||||
f'}}'
|
||||
)
|
||||
def getStyleSheet(theme=None):
|
||||
"""Return centralized progress styling for compatibility callers."""
|
||||
if theme is None:
|
||||
try:
|
||||
theme = APP().theme()
|
||||
except Exception:
|
||||
theme = AppStyleSheet.Light
|
||||
|
||||
return AppStyleSheet.progressBarStyleSheet(theme)
|
||||
|
||||
def disconnectedCallback(self):
|
||||
"""Update the auto update progress bar for a disconnected state."""
|
||||
pass
|
||||
self._setConnectionState('disconnected')
|
||||
|
||||
def connectedCallback(self):
|
||||
"""Update the auto update progress bar for a connected state."""
|
||||
pass
|
||||
self._setConnectionState('connected')
|
||||
|
||||
|
||||
class ConnectProgressBar(Mixins.QTranslatable, Mixins.ConnectionAware, QWidget):
|
||||
|
||||
Reference in New Issue
Block a user