diff --git a/novelwriter/common.py b/novelwriter/common.py index 55742b94..c148e75d 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -36,7 +36,7 @@ from configparser import ConfigParser from urllib.parse import urljoin from urllib.request import pathname2url -from PyQt5.QtGui import QDesktopServices +from PyQt5.QtGui import QColor, QDesktopServices from PyQt5.QtCore import QCoreApplication, QUrl from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout @@ -382,6 +382,11 @@ def numberToRoman(value: int, toLower: bool = False) -> str: return roman.lower() if toLower else roman +def cssCol(col: QColor, alpha: int | None = None) -> str: + """Convert a QColor object to an rgba entry to use in CSS.""" + return f"rgba({col.red()}, {col.green()}, {col.blue()}, {alpha or col.alpha()})" + + ## # Encoder Functions ## diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index 36b9d80d..108dc9f1 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -41,6 +41,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter import CONFIG, SHARED +from novelwriter.common import cssCol from novelwriter.constants import nwHeaders, nwUnicode from novelwriter.core.tohtml import ToHtml from novelwriter.enum import nwItemType, nwDocAction, nwDocMode @@ -467,35 +468,25 @@ class GuiDocViewer(QTextBrowser): """Generate an appropriate style sheet for the document viewer, based on the current syntax highlighter theme. """ - colText = SHARED.theme.colText - colHead = SHARED.theme.colHead - colVals = SHARED.theme.colVal - colMark = SHARED.theme.colMark - colKeys = SHARED.theme.colKey - colHide = SHARED.theme.colHidden - colMods = SHARED.theme.colMod - colOpts = SHARED.theme.colOpt - styleSheet = ( - "body {{color: rgb({rT}, {gT}, {bT});}}\n" - "h1, h2, h3, h4 {{color: rgb({rH}, {gH}, {bH});}}\n" - "a {{color: rgb({rA}, {gA}, {bA});}}\n" - "mark {{background-color: rgba({rE}, {gE}, {bE}, {aE});}}\n" - ".tags {{color: rgb({rK}, {gK}, {bK});}}\n" - ".optional {{color: rgb({rO}, {gO}, {bO});}}\n" - ".comment {{color: rgb({rC}, {gC}, {bC});}}\n" - ".synopsis {{color: rgb({rM}, {gM}, {bM});}}\n" - ".title {{text-align: center;}}\n" - ).format( - rT=colText.red(), gT=colText.green(), bT=colText.blue(), - rH=colHead.red(), gH=colHead.green(), bH=colHead.blue(), - rA=colVals.red(), gA=colVals.green(), bA=colVals.blue(), - rE=colMark.red(), gE=colMark.green(), bE=colMark.blue(), aE=colMark.alpha(), - rK=colKeys.red(), gK=colKeys.green(), bK=colKeys.blue(), - rC=colHide.red(), gC=colHide.green(), bC=colHide.blue(), - rM=colMods.red(), gM=colMods.green(), bM=colMods.blue(), - rO=colOpts.red(), gO=colOpts.green(), bO=colOpts.blue(), + colText = cssCol(SHARED.theme.colText) + colHead = cssCol(SHARED.theme.colHead) + colVals = cssCol(SHARED.theme.colVal) + colMark = cssCol(SHARED.theme.colMark) + colKeys = cssCol(SHARED.theme.colKey) + colOpts = cssCol(SHARED.theme.colOpt) + colHide = cssCol(SHARED.theme.colHidden) + colMods = cssCol(SHARED.theme.colMod) + self.document().setDefaultStyleSheet( + f"body {{color: {colText};}}\n" + f"h1, h2, h3, h4 {{color: {colHead};}}\n" + f"a {{color: {colVals};}}\n" + f"mark {{background-color: {colMark};}}\n" + f".tags {{color: {colKeys};}}\n" + f".optional {{color: {colOpts};}}\n" + f".comment {{color: {colHide});}}\n" + f".synopsis {{color: {colMods};}}\n" + ".title {text-align: center;}\n" ) - self.document().setDefaultStyleSheet(styleSheet) return diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index d78b84d5..30958582 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -565,7 +565,7 @@ class GuiProjectTree(QTreeWidget): # Auto Scroll self._scrollMargin = SHARED.theme.baseIconSize self._scrollDirection = 0 - self._scrollTimer = QTimer() + self._scrollTimer = QTimer(self) self._scrollTimer.timeout.connect(self._doAutoScroll) self._scrollTimer.setInterval(250) diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index 3ceb2a6f..7a841ad6 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -38,7 +38,7 @@ from PyQt5.QtGui import ( from novelwriter import CONFIG from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType from novelwriter.error import logException -from novelwriter.common import NWConfigParser, minmax +from novelwriter.common import NWConfigParser, cssCol, minmax from novelwriter.constants import nwLabels logger = logging.getLogger(__name__) @@ -440,24 +440,24 @@ class GuiTheme: # Flat Tab Widget and Tab Bar: self._styleSheets[STYLES_FLAT_TABS] = ( - "QTabWidget::pane {{border: 0;}} " - "QTabWidget QTabBar::tab {{border: 0; padding: {0}px {1}px;}} " - "QTabWidget QTabBar::tab:selected {{color: rgb({2}, {3}, {4});}} " - ).format(bPx, dPx, hCol.red(), hCol.green(), hCol.blue()) + "QTabWidget::pane {border: 0;} " + f"QTabWidget QTabBar::tab {{border: 0; padding: {bPx}px {dPx}px;}} " + f"QTabWidget QTabBar::tab:selected {{color: {cssCol(hCol)};}} " + ) # Minimal Tool Button self._styleSheets[STYLES_MIN_TOOLBUTTON] = ( - "QToolButton {{padding: {0}px; margin: 0; border: none; background: transparent;}} " - "QToolButton:hover {{border: none; background: rgba({1}, {2}, {3}, 0.2);}} " - "QToolButton::menu-indicator {{image: none;}} " - ).format(aPx, tCol.red(), tCol.green(), tCol.blue()) + f"QToolButton {{padding: {aPx}px; margin: 0; border: none; background: transparent;}} " + f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} " + "QToolButton::menu-indicator {image: none;} " + ) # Big Tool Button self._styleSheets[STYLES_BIG_TOOLBUTTON] = ( - "QToolButton {{padding: {0}px; margin: 0; border: none; background: transparent;}} " - "QToolButton:hover {{border: none; background: rgba({1}, {2}, {3}, 0.2);}} " - "QToolButton::menu-indicator {{image: none;}} " - ).format(cPx, tCol.red(), tCol.green(), tCol.blue()) + f"QToolButton {{padding: {cPx}px; margin: 0; border: none; background: transparent;}} " + f"QToolButton:hover {{border: none; background: {cssCol(tCol, 48)};}} " + "QToolButton::menu-indicator {image: none;} " + ) return diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py index ec7869f5..c71d7855 100644 --- a/novelwriter/tools/welcome.py +++ b/novelwriter/tools/welcome.py @@ -42,7 +42,7 @@ from PyQt5.QtWidgets import ( from novelwriter import CONFIG, SHARED from novelwriter.enum import nwItemClass -from novelwriter.common import formatInt, makeFileNameSafe +from novelwriter.common import cssCol, formatInt, makeFileNameSafe from novelwriter.constants import nwFiles from novelwriter.core.coretools import ProjectBuilder from novelwriter.extensions.configlayout import NWrappedWidgetBox @@ -52,7 +52,7 @@ from novelwriter.extensions.versioninfo import VersionInfoWidget logger = logging.getLogger(__name__) -PANEL_ALPHA = 0.7 +PANEL_ALPHA = 178 class GuiWelcome(QDialog): @@ -306,12 +306,12 @@ class _OpenProjectPage(QWidget): self._selectFirstItem() - baseCol = self.palette().base().color() - self.setStyleSheet(( - "QListView {{border: none; background: rgba({r},{g},{b},{a});}} " - "QLineEdit {{border: none; background: rgba({r},{g},{b},{a}); padding: {m}px;}} " - ).format(r=baseCol.red(), g=baseCol.green(), b=baseCol.blue(), - a=PANEL_ALPHA, m=CONFIG.pxInt(4))) + mPx = CONFIG.pxInt(4) + baseCol = cssCol(self.palette().base().color(), PANEL_ALPHA) + self.setStyleSheet( + f"QListView {{border: none; background: {baseCol};}} " + f"QLineEdit {{border: none; background: {baseCol}; padding: {mPx}px;}} " + ) return @@ -518,11 +518,11 @@ class _NewProjectPage(QWidget): # Styles # ====== - baseCol = self.palette().base().color() - self.setStyleSheet(( - "QScrollArea {{border: none; background: rgba({r},{g},{b},{a});}} " - "_NewProjectForm {{border: none; background: rgba({r},{g},{b},{a});}} " - ).format(r=baseCol.red(), g=baseCol.green(), b=baseCol.blue(), a=PANEL_ALPHA)) + baseCol = cssCol(self.palette().base().color(), PANEL_ALPHA) + self.setStyleSheet( + f"QScrollArea {{border: none; background: {baseCol};}} " + f"_NewProjectForm {{border: none; background: {baseCol};}} " + ) return diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index 7cadfe9e..1b2f7f9e 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -29,16 +29,16 @@ from xml.etree import ElementTree as ET from tools import writeFile from mocked import causeOSError -from PyQt5.QtGui import QDesktopServices +from PyQt5.QtGui import QColor, QDesktopServices from PyQt5.QtCore import QUrl from novelwriter.common import ( checkBool, checkFloat, checkInt, checkIntTuple, checkPath, checkString, - checkStringNone, checkUuid, formatFileFilter, formatInt, formatTime, - formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, - isItemClass, isItemLayout, isItemType, isTitleTag, jsonEncode, - makeFileNameSafe, minmax, numberToRoman, NWConfigParser, openExternalPath, - readTextFile, simplified, transferCase, xmlIndent, yesNo + checkStringNone, checkUuid, cssCol, formatFileFilter, formatInt, + formatTime, formatTimeStamp, formatVersion, fuzzyTime, getFileSize, + hexToInt, isHandle, isItemClass, isItemLayout, isItemType, isTitleTag, + jsonEncode, makeFileNameSafe, minmax, numberToRoman, NWConfigParser, + openExternalPath, readTextFile, simplified, transferCase, xmlIndent, yesNo ) @@ -494,6 +494,15 @@ def testBaseCommon_numberToRoman(): # END Test testBaseCommon_numberToRoman +@pytest.mark.base +def testBaseCommon_cssCol(): + """Test the cssCol function.""" + assert cssCol(QColor(0, 0, 0, 0)) == "rgba(0, 0, 0, 0)" + assert cssCol(QColor(10, 20, 30, 40)) == "rgba(10, 20, 30, 40)" + +# END Test testBaseCommon_cssCol + + @pytest.mark.base def testBaseCommon_jsonEncode(): """Test the jsonEncode function."""