diff --git a/novelwriter/assets/themes/default.conf b/novelwriter/assets/themes/default.conf
index 004b811d..aa3691e1 100644
--- a/novelwriter/assets/themes/default.conf
+++ b/novelwriter/assets/themes/default.conf
@@ -1,2 +1,4 @@
[Main]
-name = Default System Theme
+name = Default Theme
+description = Qt standard colours
+icontheme = typicons_light
\ No newline at end of file
diff --git a/novelwriter/assets/themes/default_dark.conf b/novelwriter/assets/themes/default_dark.conf
index 44317ac1..05a9b4ce 100644
--- a/novelwriter/assets/themes/default_dark.conf
+++ b/novelwriter/assets/themes/default_dark.conf
@@ -1,10 +1,12 @@
[Main]
-name = Default Dark Theme
-author = Veronica Berglyd Olsen
-credit = Veronica Berglyd Olsen
-url = https://github.com/vkbo/novelWriter
-license = CC BY-SA 4.0
-licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
+name = Default Dark Theme
+description = The novelWriter standard dark theme
+author = Veronica Berglyd Olsen
+credit = Veronica Berglyd Olsen
+url = https://github.com/vkbo/novelWriter
+license = CC BY-SA 4.0
+licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
+icontheme = typicons_dark
[Palette]
window = 54, 54, 54
diff --git a/novelwriter/assets/themes/default_dark.qss b/novelwriter/assets/themes/default_dark.qss
deleted file mode 100644
index 44f94ec6..00000000
--- a/novelwriter/assets/themes/default_dark.qss
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * Default Theme: Dark
- * This theme doesn't use any custom styles, so the file is only here as
- * an example. There doesn't have to be a styles.qss file in the folder.
- */
diff --git a/novelwriter/assets/themes/solarized_dark.conf b/novelwriter/assets/themes/solarized_dark.conf
index 812d968d..cf46d519 100644
--- a/novelwriter/assets/themes/solarized_dark.conf
+++ b/novelwriter/assets/themes/solarized_dark.conf
@@ -5,6 +5,7 @@ credit = Ethan Schoonover
url = https://ethanschoonover.com/solarized/
license = MIT
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
+icontheme = typicons_dark
[Palette]
window = 0, 43, 54
diff --git a/novelwriter/assets/themes/solarized_light.conf b/novelwriter/assets/themes/solarized_light.conf
index 855e4e76..c9fb50c0 100644
--- a/novelwriter/assets/themes/solarized_light.conf
+++ b/novelwriter/assets/themes/solarized_light.conf
@@ -5,6 +5,7 @@ credit = Ethan Schoonover
url = https://ethanschoonover.com/solarized/
license = MIT
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
+icontheme = typicons_light
[Palette]
window = 238, 232, 213
diff --git a/novelwriter/config.py b/novelwriter/config.py
index 73a8c3c6..d939faea 100644
--- a/novelwriter/config.py
+++ b/novelwriter/config.py
@@ -73,7 +73,6 @@ class Config:
# General
self.guiTheme = "" # GUI theme
self.guiSyntax = "" # Syntax theme
- self.guiIcons = "" # Icon theme
self.guiFont = "" # Defaults to system default font
self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class
@@ -81,7 +80,6 @@ class Config:
self.setDefaultGuiTheme()
self.setDefaultSyntaxTheme()
- self.setDefaultIconTheme()
# Localisation
self.qLocal = QLocale.system()
@@ -410,7 +408,6 @@ class Config:
cnfSec = "Main"
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
- self.guiIcons = theConf.rdStr(cnfSec, "icons", self.guiIcons)
self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
@@ -508,12 +505,6 @@ class Config:
logger.info("Using straight double quotes, so disabling auto-replace")
self.doReplaceDQuote = False
- # Check deprecated settings
- if self.guiIcons in ("typicons_colour_dark", "typicons_grey_dark"):
- self.guiIcons = "typicons_dark"
- elif self.guiIcons in ("typicons_colour_light", "typicons_grey_light"):
- self.guiIcons = "typicons_light"
-
return True
def saveConfig(self):
@@ -529,7 +520,6 @@ class Config:
"timestamp": formatTimeStamp(time()),
"theme": str(self.guiTheme),
"syntax": str(self.guiSyntax),
- "icons": str(self.guiIcons),
"guifont": str(self.guiFont),
"guifontsize": str(self.guiFontSize),
"lastnotes": str(self.lastNotes),
@@ -816,11 +806,6 @@ class Config:
"""
self.guiSyntax = "default_light"
- def setDefaultIconTheme(self):
- """Reset the icon theme to default value.
- """
- self.guiIcons = "typicons_light"
-
##
# Getters
##
diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py
index ae2df4c5..6b9dbd9f 100644
--- a/novelwriter/dialogs/preferences.py
+++ b/novelwriter/dialogs/preferences.py
@@ -34,7 +34,6 @@ from PyQt5.QtWidgets import (
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
)
-from novelwriter.enum import nwAlert
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
from novelwriter.dialogs.quotes import GuiQuoteSelect
@@ -78,10 +77,36 @@ class GuiPreferences(PagedDialog):
self.resize(*self.mainConf.getPreferencesSize())
+ # Settings
+ self._updateTheme = False
+ self._updateSyntax = False
+ self._needsRestart = False
+ self._refreshTree = False
+
logger.debug("GuiPreferences initialisation complete")
return
+ ##
+ # Properties
+ ##
+
+ @property
+ def updateTheme(self):
+ return self._updateTheme
+
+ @property
+ def updateSyntax(self):
+ return self._updateSyntax
+
+ @property
+ def needsRestart(self):
+ return self._needsRestart
+
+ @property
+ def refreshTree(self):
+ return self._refreshTree
+
##
# Slots
##
@@ -92,8 +117,7 @@ class GuiPreferences(PagedDialog):
"""
logger.debug("Saving new preferences")
- needsRestart, refreshTree = self.tabGeneral.saveValues()
-
+ self.tabGeneral.saveValues()
self.tabProjects.saveValues()
self.tabDocs.saveValues()
self.tabEditor.saveValues()
@@ -101,14 +125,6 @@ class GuiPreferences(PagedDialog):
self.tabAuto.saveValues()
self.tabQuote.saveValues()
- if needsRestart:
- self.mainGui.makeAlert(self.tr(
- "Some changes will not be applied until novelWriter has been restarted."
- ), nwAlert.INFO)
-
- if refreshTree:
- self.mainGui.projView.populateTree()
-
self._saveWindowSize()
self.accept()
@@ -140,6 +156,7 @@ class GuiPreferencesGeneral(QWidget):
super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG
+ self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme
@@ -185,22 +202,6 @@ class GuiPreferencesGeneral(QWidget):
self.tr("Requires restart.")
)
- # Select Icon Theme
- self.guiIcons = QComboBox()
- self.guiIcons.setMinimumWidth(minWidth)
- self.iconCache = self.mainTheme.iconCache.listThemes()
- for iconDir, iconName in self.iconCache:
- self.guiIcons.addItem(iconName, iconDir)
- iconIdx = self.guiIcons.findData(self.mainConf.guiIcons)
- if iconIdx != -1:
- self.guiIcons.setCurrentIndex(iconIdx)
-
- self.mainForm.addRow(
- self.tr("Main icon theme"),
- self.guiIcons,
- self.tr("Requires restart.")
- )
-
# Editor Theme
self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
@@ -288,27 +289,21 @@ class GuiPreferencesGeneral(QWidget):
"""
guiLang = self.guiLang.currentData()
guiTheme = self.guiTheme.currentData()
- guiIcons = self.guiIcons.currentData()
guiSyntax = self.guiSyntax.currentData()
guiFont = self.guiFont.text()
guiFontSize = self.guiFontSize.value()
emphLabels = self.emphLabels.isChecked()
- # Check if restart is needed
- needsRestart = False
- needsRestart |= self.mainConf.guiLang != guiLang
- needsRestart |= self.mainConf.guiTheme != guiTheme
- needsRestart |= self.mainConf.guiIcons != guiIcons
- needsRestart |= self.mainConf.guiFont != guiFont
- needsRestart |= self.mainConf.guiFontSize != guiFontSize
-
- # Check if refreshing project tree is needed
- refreshTree = False
- refreshTree |= self.mainConf.emphLabels != emphLabels
+ # Update Flags
+ self.prefsGui._updateTheme |= self.mainConf.guiTheme != guiTheme
+ self.prefsGui._updateSyntax |= self.mainConf.guiSyntax != guiSyntax
+ self.prefsGui._needsRestart |= self.mainConf.guiLang != guiLang
+ self.prefsGui._needsRestart |= self.mainConf.guiFont != guiFont
+ self.prefsGui._needsRestart |= self.mainConf.guiFontSize != guiFontSize
+ self.prefsGui._refreshTree |= self.mainConf.emphLabels != emphLabels
self.mainConf.guiLang = guiLang
self.mainConf.guiTheme = guiTheme
- self.mainConf.guiIcons = guiIcons
self.mainConf.guiSyntax = guiSyntax
self.mainConf.guiFont = guiFont
self.mainConf.guiFontSize = guiFontSize
@@ -319,7 +314,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainConf.confChanged = True
- return needsRestart, refreshTree
+ return
##
# Slots
diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py
index 564e23c3..3e4a965f 100644
--- a/novelwriter/gui/doceditor.py
+++ b/novelwriter/gui/doceditor.py
@@ -138,24 +138,20 @@ class GuiDocEditor(QTextEdit):
self.setFrameStyle(QFrame.NoFrame)
# Custom Shortcuts
- QShortcut(
- QKeySequence("Ctrl+."),
- self,
- context=Qt.WidgetShortcut,
- activated=self._openSpellContext
- )
- QShortcut(
- Qt.Key_Return | Qt.ControlModifier,
- self,
- context=Qt.WidgetShortcut,
- activated=self._followTag
- )
- QShortcut(
- Qt.Key_Enter | Qt.ControlModifier,
- self,
- context=Qt.WidgetShortcut,
- activated=self._followTag
- )
+ self.keyContext = QShortcut(self)
+ self.keyContext.setKey("Ctrl+.")
+ self.keyContext.setContext(Qt.WidgetShortcut)
+ self.keyContext.activated.connect(self._openSpellContext)
+
+ self.followTag1 = QShortcut(self)
+ self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier)
+ self.followTag1.setContext(Qt.WidgetShortcut)
+ self.followTag1.activated.connect(self._followTag)
+
+ self.followTag2 = QShortcut(self)
+ self.followTag2.setKey(Qt.Key_Enter | Qt.ControlModifier)
+ self.followTag2.setContext(Qt.WidgetShortcut)
+ self.followTag2.activated.connect(self._followTag)
# Set Up Document Word Counter
self.wcTimerDoc = QTimer()
@@ -177,6 +173,7 @@ class GuiDocEditor(QTextEdit):
self.wCounterSel.signals.countsReady.connect(self._updateSelCounts)
# Finalise
+ self.updateSyntaxColours()
self.initEditor()
logger.debug("GuiDocEditor initialisation complete")
@@ -210,6 +207,35 @@ class GuiDocEditor(QTextEdit):
return True
+ def updateTheme(self):
+ """Update theme elements
+ """
+ self.docSearch.updateTheme()
+ self.docHeader.updateTheme()
+ self.docFooter.updateTheme()
+ return
+
+ def updateSyntaxColours(self):
+ """Update the syntax highlighting theme.
+ """
+ mainPalette = self.palette()
+ mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
+ mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
+ mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
+ self.setPalette(mainPalette)
+
+ docPalette = self.viewport().palette()
+ docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
+ docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
+ self.viewport().setPalette(docPalette)
+
+ self.docHeader.matchColours()
+ self.docFooter.matchColours()
+
+ self.highLight.initHighlighter()
+
+ return
+
def initEditor(self):
"""Initialise or re-initialise the editor with the user's
settings. This function is both called when the editor is
@@ -256,21 +282,6 @@ class GuiDocEditor(QTextEdit):
theFont.setPointSize(self.mainConf.textSize)
self.setFont(theFont)
- # Set the widget colours to match syntax theme
- mainPalette = self.palette()
- mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
- mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
- mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
- self.setPalette(mainPalette)
-
- docPalette = self.viewport().palette()
- docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
- docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
- self.viewport().setPalette(docPalette)
-
- self.docHeader.matchColours()
- self.docFooter.matchColours()
-
# Set default text margins
# Due to cursor visibility, a part of the margin must be
# allocated to the document itself. See issue #1112.
@@ -305,9 +316,6 @@ class GuiDocEditor(QTextEdit):
# Refresh the tab stops
self.setTabStopDistance(self.mainConf.getTabWidth())
- # Initialise the syntax highlighter
- self.highLight.initHighlighter()
-
# Configure word count timer
self.wcInterval = self.mainConf.wordCountTimer
self.wcTimerDoc.setInterval(int(self.wcInterval*1000))
@@ -1162,6 +1170,7 @@ class GuiDocEditor(QTextEdit):
posCursor = self.cursorForPosition(thePos)
spellCheck = self._spellCheck
+ theWord = ""
if posCursor.block().text().startswith("@"):
spellCheck = False
@@ -1430,6 +1439,7 @@ class GuiDocEditor(QTextEdit):
origB = theCursor.selectionEnd()
else:
origA = theCursor.position()
+ origB = theCursor.position()
findOpt = QTextDocument.FindFlag(0)
if self.docSearch.isCaseSense:
@@ -2253,7 +2263,6 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchOpt.setIconSize(QSize(tPx, tPx))
self.searchOpt.setContentsMargins(0, 0, 0, 0)
- self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
self.searchLabel = QLabel(self.tr("Search"))
self.searchLabel.setFont(self.boxFont)
@@ -2264,35 +2273,30 @@ class GuiDocEditSearch(QFrame):
self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont))
self.toggleCase = QAction(self.tr("Case Sensitive"), self)
- self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction(self.tr("Whole Words Only"), self)
- self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
- self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction(self.tr("Loop Search"), self)
- self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction(self.tr("Search Next File"), self)
- self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile)
self.toggleProject.toggled.connect(self._doToggleProject)
@@ -2301,7 +2305,6 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator()
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
- self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap)
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
@@ -2310,7 +2313,6 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator()
self.cancelSearch = QAction(self.tr("Close Search"), self)
- self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch)
@@ -2322,15 +2324,14 @@ class GuiDocEditSearch(QFrame):
self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow)
self.showReplace.setCheckable(True)
- self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace)
- self.searchButton = QPushButton(self.mainTheme.getIcon("search"), "")
+ self.searchButton = QPushButton("")
self.searchButton.setFixedSize(QSize(bPx, bPx))
self.searchButton.setToolTip(self.tr("Find in current document"))
self.searchButton.clicked.connect(self._doSearch)
- self.replaceButton = QPushButton(self.mainTheme.getIcon("search_replace"), "")
+ self.replaceButton = QPushButton("")
self.replaceButton.setFixedSize(QSize(bPx, bPx))
self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
self.replaceButton.clicked.connect(self._doReplace)
@@ -2360,6 +2361,35 @@ class GuiDocEditSearch(QFrame):
self.replaceButton.setVisible(False)
self.adjustSize()
+ self.updateTheme()
+
+ logger.debug("GuiDocEditSearch initialisation complete")
+
+ return
+
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ qPalette = qApp.palette()
+ self.setPalette(qPalette)
+ self.searchBox.setPalette(qPalette)
+ self.replaceBox.setPalette(qPalette)
+
+ # Set icons
+ self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
+ self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
+ self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
+ self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
+ self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
+ self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
+ self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
+ self.searchButton.setIcon(self.mainTheme.getIcon("search"))
+ self.replaceButton.setIcon(self.mainTheme.getIcon("search_replace"))
+
+ # Set stylesheets
+ self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
+ self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
+
# Construct Box Colours
qPalette = self.searchBox.palette()
baseCol = qPalette.base().color()
@@ -2378,8 +2408,6 @@ class GuiDocEditSearch(QFrame):
False: errCol
}
- logger.debug("GuiDocEditSearch initialisation complete")
-
return
def closeSearch(self):
@@ -2405,10 +2433,10 @@ class GuiDocEditSearch(QFrame):
"""
if self.replaceBox.isVisible():
if self.searchBox.hasFocus():
- self.replaceBox.setFocus(True)
+ self.replaceBox.setFocus()
return True
elif self.replaceBox.hasFocus():
- self.searchBox.setFocus(True)
+ self.searchBox.setFocus()
return True
return False
@@ -2498,12 +2526,14 @@ class GuiDocEditSearch(QFrame):
# Slots
##
+ @pyqtSlot()
def _doClose(self):
"""Hide the search/replace bar.
"""
self.closeSearch()
return
+ @pyqtSlot()
def _doSearch(self):
"""Call the search action function for the document editor.
"""
@@ -2514,12 +2544,14 @@ class GuiDocEditSearch(QFrame):
self.docEditor.findNext()
return
+ @pyqtSlot()
def _doReplace(self):
"""Call the replace action function for the document editor.
"""
self.docEditor.replaceNext()
return
+ @pyqtSlot(bool)
def _doToggleReplace(self, theState):
"""Toggle the show/hide of the replace box.
"""
@@ -2534,36 +2566,42 @@ class GuiDocEditSearch(QFrame):
self.docEditor.updateDocMargins()
return
+ @pyqtSlot(bool)
def _doToggleCase(self, theState):
"""Enable/disable case sensitive mode.
"""
self.isCaseSense = theState
return
+ @pyqtSlot(bool)
def _doToggleWord(self, theState):
"""Enable/disable whole word search mode.
"""
self.isWholeWord = theState
return
+ @pyqtSlot(bool)
def _doToggleRegEx(self, theState):
"""Enable/disable regular expression search mode.
"""
self.isRegEx = theState
return
+ @pyqtSlot(bool)
def _doToggleLoop(self, theState):
"""Enable/disable looping the search.
"""
self.doLoop = theState
return
+ @pyqtSlot(bool)
def _doToggleProject(self, theState):
"""Enable/disable continuing search in next project file.
"""
self.doNextFile = theState
return
+ @pyqtSlot(bool)
def _doToggleMatchCap(self, theState):
"""Enable/disable preserving capitalisation when replacing.
"""
@@ -2626,51 +2664,38 @@ class GuiDocEditHeader(QWidget):
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.theTitle.setFont(lblFont)
- buttonStyle = (
- "QToolButton {{border: none; background: transparent;}} "
- "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
- ).format(*self.mainTheme.colText)
-
# Buttons
self.editButton = QToolButton(self)
- self.editButton.setIcon(self.mainTheme.getIcon("edit"))
self.editButton.setContentsMargins(0, 0, 0, 0)
self.editButton.setIconSize(QSize(fPx, fPx))
self.editButton.setFixedSize(fPx, fPx)
- self.editButton.setStyleSheet(buttonStyle)
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.editButton.setVisible(False)
self.editButton.setToolTip(self.tr("Edit document label"))
self.editButton.clicked.connect(self._editDocument)
self.searchButton = QToolButton(self)
- self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.searchButton.setContentsMargins(0, 0, 0, 0)
self.searchButton.setIconSize(QSize(fPx, fPx))
self.searchButton.setFixedSize(fPx, fPx)
- self.searchButton.setStyleSheet(buttonStyle)
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchButton.setVisible(False)
self.searchButton.setToolTip(self.tr("Search document"))
self.searchButton.clicked.connect(self._searchDocument)
self.minmaxButton = QToolButton(self)
- self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
self.minmaxButton.setContentsMargins(0, 0, 0, 0)
self.minmaxButton.setIconSize(QSize(fPx, fPx))
self.minmaxButton.setFixedSize(fPx, fPx)
- self.minmaxButton.setStyleSheet(buttonStyle)
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.minmaxButton.setVisible(False)
self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
self.minmaxButton.clicked.connect(self._minmaxDocument)
self.closeButton = QToolButton(self)
- self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx)
- self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False)
self.closeButton.setToolTip(self.tr("Close the document"))
@@ -2693,8 +2718,7 @@ class GuiDocEditHeader(QWidget):
self.outerBox.setContentsMargins(cM, cM, cM, cM)
self.setMinimumHeight(fPx + 2*cM)
- # Fix the Colours
- self.matchColours()
+ self.updateTheme()
logger.debug("GuiDocEditHeader initialisation complete")
@@ -2704,6 +2728,28 @@ class GuiDocEditHeader(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.editButton.setIcon(self.mainTheme.getIcon("edit"))
+ self.searchButton.setIcon(self.mainTheme.getIcon("search"))
+ self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
+ self.closeButton.setIcon(self.mainTheme.getIcon("close"))
+
+ buttonStyle = (
+ "QToolButton {{border: none; background: transparent;}} "
+ "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
+ ).format(*self.mainTheme.colText)
+
+ self.editButton.setStyleSheet(buttonStyle)
+ self.searchButton.setStyleSheet(buttonStyle)
+ self.minmaxButton.setStyleSheet(buttonStyle)
+ self.closeButton.setStyleSheet(buttonStyle)
+
+ self.matchColours()
+
+ return
+
def matchColours(self):
"""Update the colours of the widget to match those of the syntax
theme rather than the main GUI.
@@ -2768,18 +2814,21 @@ class GuiDocEditHeader(QWidget):
# Slots
##
+ @pyqtSlot()
def _editDocument(self):
"""Open the edit item dialog from the main GUI.
"""
self.mainGui.editItemLabel(self._docHandle)
return
+ @pyqtSlot()
def _searchDocument(self):
"""Toggle the visibility of the search box.
"""
self.docEditor.toggleSearch()
return
+ @pyqtSlot()
def _closeDocument(self):
"""Trigger the close editor on the main window.
"""
@@ -2790,6 +2839,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setVisible(False)
return
+ @pyqtSlot()
def _minmaxDocument(self):
"""Switch on or off Focus Mode.
"""
@@ -2862,7 +2912,6 @@ class GuiDocEditFooter(QWidget):
# Lines
self.linesIcon = QLabel("")
- self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.linesIcon.setContentsMargins(0, 0, 0, 0)
self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
@@ -2878,7 +2927,6 @@ class GuiDocEditFooter(QWidget):
# Words
self.wordsIcon = QLabel("")
- self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.wordsIcon.setContentsMargins(0, 0, 0, 0)
self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
@@ -2913,7 +2961,7 @@ class GuiDocEditFooter(QWidget):
self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours
- self.matchColours()
+ self.updateTheme()
self.updateLineCount()
self.updateCounts()
@@ -2925,6 +2973,16 @@ class GuiDocEditFooter(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
+ self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
+
+ self.matchColours()
+
+ return
+
def matchColours(self):
"""Update the colours of the widget to match those of the syntax
theme rather than the main GUI.
diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py
index 644c3b6d..0437363e 100644
--- a/novelwriter/gui/docviewer.py
+++ b/novelwriter/gui/docviewer.py
@@ -102,6 +102,13 @@ class GuiDocViewer(QTextBrowser):
self.docHeader.setTitleFromHandle(self._docHandle)
return True
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.docHeader.updateTheme()
+ self.docFooter.updateTheme()
+ return
+
def initViewer(self):
"""Set editor settings from main config.
"""
@@ -735,51 +742,38 @@ class GuiDocViewHeader(QWidget):
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.theTitle.setFont(lblFont)
- buttonStyle = (
- "QToolButton {{border: none; background: transparent;}} "
- "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
- ).format(*self.mainTheme.colText)
-
# Buttons
self.backButton = QToolButton(self)
- self.backButton.setIcon(self.mainTheme.getIcon("backward"))
self.backButton.setContentsMargins(0, 0, 0, 0)
self.backButton.setIconSize(QSize(fPx, fPx))
self.backButton.setFixedSize(fPx, fPx)
- self.backButton.setStyleSheet(buttonStyle)
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.backButton.setVisible(False)
self.backButton.setToolTip(self.tr("Go backward"))
self.backButton.clicked.connect(self.docViewer.navBackward)
self.forwardButton = QToolButton(self)
- self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
self.forwardButton.setContentsMargins(0, 0, 0, 0)
self.forwardButton.setIconSize(QSize(fPx, fPx))
self.forwardButton.setFixedSize(fPx, fPx)
- self.forwardButton.setStyleSheet(buttonStyle)
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.forwardButton.setVisible(False)
self.forwardButton.setToolTip(self.tr("Go forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward)
self.refreshButton = QToolButton(self)
- self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
self.refreshButton.setContentsMargins(0, 0, 0, 0)
self.refreshButton.setIconSize(QSize(fPx, fPx))
self.refreshButton.setFixedSize(fPx, fPx)
- self.refreshButton.setStyleSheet(buttonStyle)
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.refreshButton.setVisible(False)
self.refreshButton.setToolTip(self.tr("Reload the document"))
self.refreshButton.clicked.connect(self._refreshDocument)
self.closeButton = QToolButton(self)
- self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx)
- self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False)
self.closeButton.setToolTip(self.tr("Close the document"))
@@ -803,7 +797,7 @@ class GuiDocViewHeader(QWidget):
self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours
- self.matchColours()
+ self.updateTheme()
logger.debug("GuiDocViewHeader initialisation complete")
@@ -813,6 +807,28 @@ class GuiDocViewHeader(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.backButton.setIcon(self.mainTheme.getIcon("backward"))
+ self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
+ self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
+ self.closeButton.setIcon(self.mainTheme.getIcon("close"))
+
+ buttonStyle = (
+ "QToolButton {{border: none; background: transparent;}} "
+ "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
+ ).format(*self.mainTheme.colText)
+
+ self.backButton.setStyleSheet(buttonStyle)
+ self.forwardButton.setStyleSheet(buttonStyle)
+ self.refreshButton.setStyleSheet(buttonStyle)
+ self.closeButton.setStyleSheet(buttonStyle)
+
+ self.matchColours()
+
+ return
+
def matchColours(self):
"""Update the colours of the widget to match those of the syntax
theme rather than the main GUI.
@@ -873,12 +889,14 @@ class GuiDocViewHeader(QWidget):
# Slots
##
+ @pyqtSlot()
def _closeDocument(self):
"""Trigger the close editor/viewer on the main window.
"""
self.mainGui.closeDocViewer()
return
+ @pyqtSlot()
def _refreshDocument(self):
"""Reload the content of the document.
"""
@@ -926,33 +944,13 @@ class GuiDocViewFooter(QWidget):
bSp = self.mainConf.pxInt(2)
hSp = self.mainConf.pxInt(8)
- # Icons
- stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
- stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
- stickyIcon = QIcon()
- stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
- stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
-
- bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
- bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
- bulletIcon = QIcon()
- bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
- bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
-
# Main Widget Settings
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
- buttonStyle = (
- "QToolButton {{border: none; background: transparent;}} "
- "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
- ).format(*self.mainTheme.colText)
-
# Show/Hide Details
self.showHide = QToolButton(self)
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
- self.showHide.setStyleSheet(buttonStyle)
- self.showHide.setIcon(self.mainTheme.getIcon("reference"))
self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(self._doShowHide)
@@ -962,8 +960,6 @@ class GuiDocViewFooter(QWidget):
self.stickyRefs = QToolButton(self)
self.stickyRefs.setCheckable(True)
self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly)
- self.stickyRefs.setStyleSheet(buttonStyle)
- self.stickyRefs.setIcon(stickyIcon)
self.stickyRefs.setIconSize(QSize(fPx, fPx))
self.stickyRefs.setFixedSize(QSize(fPx, fPx))
self.stickyRefs.toggled.connect(self._doToggleSticky)
@@ -976,8 +972,6 @@ class GuiDocViewFooter(QWidget):
self.showComments.setCheckable(True)
self.showComments.setChecked(self.mainConf.viewComments)
self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly)
- self.showComments.setStyleSheet(buttonStyle)
- self.showComments.setIcon(bulletIcon)
self.showComments.setIconSize(QSize(fPx, fPx))
self.showComments.setFixedSize(QSize(fPx, fPx))
self.showComments.toggled.connect(self._doToggleComments)
@@ -988,8 +982,6 @@ class GuiDocViewFooter(QWidget):
self.showSynopsis.setCheckable(True)
self.showSynopsis.setChecked(self.mainConf.viewSynopsis)
self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly)
- self.showSynopsis.setStyleSheet(buttonStyle)
- self.showSynopsis.setIcon(bulletIcon)
self.showSynopsis.setIconSize(QSize(fPx, fPx))
self.showSynopsis.setFixedSize(QSize(fPx, fPx))
self.showSynopsis.toggled.connect(self._doToggleSynopsis)
@@ -1063,7 +1055,7 @@ class GuiDocViewFooter(QWidget):
self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours
- self.matchColours()
+ self.updateTheme()
logger.debug("GuiDocViewFooter initialisation complete")
@@ -1073,6 +1065,46 @@ class GuiDocViewFooter(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ # Icons
+
+ fPx = int(0.9*self.mainTheme.fontPixelSize)
+
+ stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
+ stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
+ stickyIcon = QIcon()
+ stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
+ stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
+
+ bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
+ bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
+ bulletIcon = QIcon()
+ bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
+ bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
+
+ self.showHide.setIcon(self.mainTheme.getIcon("reference"))
+ self.stickyRefs.setIcon(stickyIcon)
+ self.showComments.setIcon(bulletIcon)
+ self.showSynopsis.setIcon(bulletIcon)
+
+ # StyleSheets
+
+ buttonStyle = (
+ "QToolButton {{border: none; background: transparent;}} "
+ "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
+ ).format(*self.mainTheme.colText)
+
+ self.showHide.setStyleSheet(buttonStyle)
+ self.stickyRefs.setStyleSheet(buttonStyle)
+ self.showComments.setStyleSheet(buttonStyle)
+ self.showSynopsis.setStyleSheet(buttonStyle)
+
+ self.matchColours()
+
+ return
+
def matchColours(self):
"""Update the colours of the widget to match those of the syntax
theme rather than the main GUI.
@@ -1094,6 +1126,7 @@ class GuiDocViewFooter(QWidget):
# Slots
##
+ @pyqtSlot()
def _doShowHide(self):
"""Toggle the expand/collapse of the panel.
"""
@@ -1101,6 +1134,7 @@ class GuiDocViewFooter(QWidget):
self.viewMeta.setVisible(not isVisible)
return
+ @pyqtSlot(bool)
def _doToggleSticky(self, theState):
"""Toggle the sticky flag for the reference panel.
"""
@@ -1110,6 +1144,7 @@ class GuiDocViewFooter(QWidget):
self.viewMeta.refreshReferences(self.docViewer.docHandle())
return
+ @pyqtSlot(bool)
def _doToggleComments(self, theState):
"""Toggle the view comment button and reload the document.
"""
@@ -1117,6 +1152,7 @@ class GuiDocViewFooter(QWidget):
self.docViewer.reloadText()
return
+ @pyqtSlot(bool)
def _doToggleSynopsis(self, theState):
"""Toggle the view synopsis button and reload the document.
"""
diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py
index a99650c2..e8eae31d 100644
--- a/novelwriter/gui/itemdetails.py
+++ b/novelwriter/gui/itemdetails.py
@@ -53,12 +53,8 @@ class GuiItemDetails(QWidget):
hSp = self.mainConf.pxInt(6)
vSp = self.mainConf.pxInt(1)
mPx = self.mainConf.pxInt(6)
- iPx = self.mainTheme.baseIconSize
fPt = self.mainTheme.fontPointSize
- self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
- self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
-
fntLabel = QFont()
fntLabel.setBold(True)
fntLabel.setPointSizeF(0.9*fPt)
@@ -179,6 +175,8 @@ class GuiItemDetails(QWidget):
self.setLayout(self.mainBox)
+ self.updateTheme()
+
# Make sure the columns for flags and counts don't resize too often
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
@@ -219,6 +217,15 @@ class GuiItemDetails(QWidget):
"""
self.updateViewBox(self._itemHandle)
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ iPx = self.mainTheme.baseIconSize
+ self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
+ self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
+ self.updateViewBox(self._itemHandle)
+ return
+
##
# Public Slots
##
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index acadd29f..210626b5 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -92,6 +92,14 @@ class GuiNovelView(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.novelBar.updateTheme()
+ self.novelTree.updateTheme()
+ self.refreshTree()
+ return
+
def initSettings(self):
"""Initialise GUI elements that depend on specific settings.
"""
@@ -181,16 +189,6 @@ class GuiNovelToolBar(QWidget):
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
- qPalette = self.palette()
- qPalette.setBrush(QPalette.Window, qPalette.base())
- self.setPalette(qPalette)
-
- fadeCol = qPalette.text().color()
- buttonStyle = (
- "QToolButton {{padding: {0}px; border: none; background: transparent;}} "
- "QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
- ).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
-
# Widget Label
self.viewLabel = QLabel("%s" % self.tr("Novel Outline"))
self.viewLabel.setContentsMargins(0, 0, 0, 0)
@@ -199,9 +197,7 @@ class GuiNovelToolBar(QWidget):
# Refresh Button
self.tbRefresh = QToolButton(self)
self.tbRefresh.setToolTip(self.tr("Refresh"))
- self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbRefresh.setIconSize(QSize(iPx, iPx))
- self.tbRefresh.setStyleSheet(buttonStyle)
self.tbRefresh.clicked.connect(self._refreshNovelTree)
# Novel Root Menu
@@ -211,9 +207,7 @@ class GuiNovelToolBar(QWidget):
self.tbRoot = QToolButton(self)
self.tbRoot.setToolTip(self.tr("Novel Root"))
- self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
self.tbRoot.setIconSize(QSize(iPx, iPx))
- self.tbRoot.setStyleSheet(buttonStyle)
self.tbRoot.setMenu(self.mRoot)
self.tbRoot.setPopupMode(QToolButton.InstantPopup)
@@ -230,9 +224,7 @@ class GuiNovelToolBar(QWidget):
self.tbMore = QToolButton(self)
self.tbMore.setToolTip(self.tr("More Options"))
- self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbMore.setIconSize(QSize(iPx, iPx))
- self.tbMore.setStyleSheet(buttonStyle)
self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup)
@@ -247,6 +239,8 @@ class GuiNovelToolBar(QWidget):
self.setLayout(self.outerBox)
+ self.updateTheme()
+
logger.debug("GuiNovelToolBar initialisation complete")
return
@@ -255,6 +249,31 @@ class GuiNovelToolBar(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ # Icons
+ self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
+ self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
+ self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
+
+ qPalette = self.palette()
+ qPalette.setBrush(QPalette.Window, qPalette.base())
+ self.setPalette(qPalette)
+
+ # StyleSheets
+ fadeCol = qPalette.text().color()
+ buttonStyle = (
+ "QToolButton {{padding: {0}px; border: none; background: transparent;}} "
+ "QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
+ ).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
+
+ self.tbRefresh.setStyleSheet(buttonStyle)
+ self.tbRoot.setStyleSheet(buttonStyle)
+ self.tbMore.setStyleSheet(buttonStyle)
+
+ return
+
def clearContent(self):
"""Run clearing project tasks.
"""
@@ -390,7 +409,6 @@ class GuiNovelTree(QTreeWidget):
fH2.setBold(True)
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
- self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
# Connect signals
self.clicked.connect(self._treeItemClicked)
@@ -399,6 +417,7 @@ class GuiNovelTree(QTreeWidget):
# Set custom settings
self.initSettings()
+ self.updateTheme()
logger.debug("GuiNovelTree initialisation complete")
@@ -420,6 +439,13 @@ class GuiNovelTree(QTreeWidget):
return
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ iPx = self.mainTheme.baseIconSize
+ self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
+ return
+
##
# Properties
##
diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py
index bb900d9f..dc0bf6d6 100644
--- a/novelwriter/gui/outline.py
+++ b/novelwriter/gui/outline.py
@@ -97,6 +97,13 @@ class GuiOutlineView(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.outlineBar.updateTheme()
+ self.refreshTree()
+ return
+
def initSettings(self):
"""Initialise GUI elements that depend on specific settings.
"""
@@ -213,7 +220,6 @@ class GuiOutlineToolBar(QToolBar):
self.setMovable(False)
self.setIconSize(QSize(iPx, iPx))
self.setContentsMargins(0, 0, 0, 0)
- self.setStyleSheet("QToolBar {border: 0px;}")
stretch = QWidget(self)
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -228,7 +234,6 @@ class GuiOutlineToolBar(QToolBar):
# Actions
self.aRefresh = QAction(self.tr("Refresh"), self)
- self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.aRefresh.triggered.connect(self._refreshRequested)
# Column Menu
@@ -238,7 +243,6 @@ class GuiOutlineToolBar(QToolBar):
)
self.tbColumns = QToolButton(self)
- self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
self.tbColumns.setMenu(self.mColumns)
self.tbColumns.setPopupMode(QToolButton.InstantPopup)
@@ -258,6 +262,16 @@ class GuiOutlineToolBar(QToolBar):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.setStyleSheet("QToolBar {border: 0px;}")
+
+ self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
+ self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
+
+ return
+
def populateNovelList(self):
"""Fill the novel combo box with a list of all novel folders.
"""
diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py
index e34419d1..19d8af38 100644
--- a/novelwriter/gui/projtree.py
+++ b/novelwriter/gui/projtree.py
@@ -119,6 +119,13 @@ class GuiProjectView(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ self.projBar.updateTheme()
+ self.populateTree()
+ return
+
def initSettings(self):
"""Initialise GUI elements that depend on specific settings.
"""
@@ -213,16 +220,6 @@ class GuiProjectToolBar(QWidget):
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
- qPalette = self.palette()
- qPalette.setBrush(QPalette.Window, qPalette.base())
- self.setPalette(qPalette)
-
- fadeCol = qPalette.text().color()
- buttonStyle = (
- "QToolButton {{padding: {0}px; border: none; background: transparent;}} "
- "QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
- ).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
-
# Widget Label
self.viewLabel = QLabel("%s" % self.tr("Project Content"))
self.viewLabel.setContentsMargins(0, 0, 0, 0)
@@ -234,78 +231,56 @@ class GuiProjectToolBar(QWidget):
self.tbQuick = QToolButton(self)
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
self.tbQuick.setShortcut("Ctrl+L")
- self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
self.tbQuick.setIconSize(QSize(iPx, iPx))
- self.tbQuick.setStyleSheet(buttonStyle)
self.tbQuick.setMenu(self.mQuick)
self.tbQuick.setPopupMode(QToolButton.InstantPopup)
# Move Buttons
self.tbMoveU = QToolButton(self)
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
- self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
self.tbMoveU.setIconSize(QSize(iPx, iPx))
- self.tbMoveU.setStyleSheet(buttonStyle)
self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1))
self.tbMoveD = QToolButton(self)
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
- self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
self.tbMoveD.setIconSize(QSize(iPx, iPx))
- self.tbMoveD.setStyleSheet(buttonStyle)
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
# Add Item Menu
self.mAdd = QMenu()
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
- self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
self.aAddEmpty.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False)
)
self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
- self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
self.aAddChap.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False)
)
self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
- self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
self.aAddScene.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False)
)
self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
- self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
self.aAddNote.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
)
self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
- self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
self.aAddFolder.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FOLDER)
)
self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
- self._addRootFolderEntry(nwItemClass.NOVEL)
- self._addRootFolderEntry(nwItemClass.ARCHIVE)
- self.mAddRoot.addSeparator()
- self._addRootFolderEntry(nwItemClass.PLOT)
- self._addRootFolderEntry(nwItemClass.CHARACTER)
- self._addRootFolderEntry(nwItemClass.WORLD)
- self._addRootFolderEntry(nwItemClass.TIMELINE)
- self._addRootFolderEntry(nwItemClass.OBJECT)
- self._addRootFolderEntry(nwItemClass.ENTITY)
- self._addRootFolderEntry(nwItemClass.CUSTOM)
+ self._buildRootMenu()
self.tbAdd = QToolButton(self)
self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
self.tbAdd.setShortcut("Ctrl+N")
- self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
self.tbAdd.setIconSize(QSize(iPx, iPx))
- self.tbAdd.setStyleSheet(buttonStyle)
self.tbAdd.setMenu(self.mAdd)
self.tbAdd.setPopupMode(QToolButton.InstantPopup)
@@ -326,9 +301,7 @@ class GuiProjectToolBar(QWidget):
self.tbMore = QToolButton(self)
self.tbMore.setToolTip(self.tr("More Options"))
- self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbMore.setIconSize(QSize(iPx, iPx))
- self.tbMore.setStyleSheet(buttonStyle)
self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup)
@@ -344,6 +317,7 @@ class GuiProjectToolBar(QWidget):
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
+ self.updateTheme()
logger.debug("GuiProjectToolBar initialisation complete")
@@ -353,6 +327,41 @@ class GuiProjectToolBar(QWidget):
# Methods
##
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ qPalette = self.palette()
+ qPalette.setBrush(QPalette.Window, qPalette.base())
+ self.setPalette(qPalette)
+
+ fadeCol = qPalette.text().color()
+ buttonStyle = (
+ "QToolButton {{padding: {0}px; border: none; background: transparent;}} "
+ "QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
+ ).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
+
+ self.tbQuick.setStyleSheet(buttonStyle)
+ self.tbMoveU.setStyleSheet(buttonStyle)
+ self.tbMoveD.setStyleSheet(buttonStyle)
+ self.tbAdd.setStyleSheet(buttonStyle)
+ self.tbMore.setStyleSheet(buttonStyle)
+
+ self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
+ self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
+ self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
+ self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
+ self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
+ self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
+ self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
+ self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
+ self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
+ self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
+
+ self.buildQuickLinkMenu()
+ self._buildRootMenu()
+
+ return
+
def clearContent(self):
"""Clear dynamic content on the tool bar.
"""
@@ -379,13 +388,29 @@ class GuiProjectToolBar(QWidget):
# Internal Functions
##
- def _addRootFolderEntry(self, itemClass):
- """Add a menu entry for a root folder of a given class.
+ def _buildRootMenu(self):
+ """Build the rood folder menu.
"""
- aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
- aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass]))
- aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
- self.mAddRoot.addAction(aNew)
+ def addClass(itemClass):
+ aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
+ aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass]))
+ aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
+ self.mAddRoot.addAction(aNew)
+ return
+
+ self.mAddRoot.clear()
+ addClass(nwItemClass.NOVEL)
+ addClass(nwItemClass.ARCHIVE)
+ self.mAddRoot.addSeparator()
+ addClass(nwItemClass.PLOT)
+ addClass(nwItemClass.CHARACTER)
+ addClass(nwItemClass.WORLD)
+ addClass(nwItemClass.TIMELINE)
+ addClass(nwItemClass.OBJECT)
+ addClass(nwItemClass.ENTITY)
+ addClass(nwItemClass.CUSTOM)
+
+ return
# END Class GuiProjectToolBar
diff --git a/novelwriter/gui/statusbar.py b/novelwriter/gui/statusbar.py
index c6191b8d..26830e2d 100644
--- a/novelwriter/gui/statusbar.py
+++ b/novelwriter/gui/statusbar.py
@@ -66,7 +66,6 @@ class GuiMainStatus(QStatusBar):
# The Spell Checker Language
self.langIcon = QLabel("")
self.langText = QLabel(self.tr("None"))
- self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.langIcon)
@@ -91,7 +90,6 @@ class GuiMainStatus(QStatusBar):
# The Project and Session Stats
self.statsIcon = QLabel()
self.statsText = QLabel("")
- self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
self.statsIcon.setContentsMargins(0, 0, 0, 0)
self.statsText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.statsIcon)
@@ -99,12 +97,8 @@ class GuiMainStatus(QStatusBar):
# The Session Clock
# Set the mimimum width so the label doesn't rescale every second
- self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
- self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
-
self.timeIcon = QLabel()
self.timeText = QLabel("")
- self.timeIcon.setPixmap(self.timePixmap)
self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0)
@@ -117,6 +111,7 @@ class GuiMainStatus(QStatusBar):
logger.debug("GuiMainStatus initialisation complete")
+ self.updateTheme()
self.clearStatus()
return
@@ -132,6 +127,21 @@ class GuiMainStatus(QStatusBar):
self.updateTime()
return True
+ def updateTheme(self):
+ """Update theme elements.
+ """
+ iPx = self.mainTheme.baseIconSize
+
+ self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
+ self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
+
+ self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
+ self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
+
+ self.timeIcon.setPixmap(self.timePixmap)
+
+ return
+
##
# Setters
##
diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py
index 4c76179d..93c26440 100644
--- a/novelwriter/gui/theme.py
+++ b/novelwriter/gui/theme.py
@@ -38,7 +38,7 @@ from PyQt5.QtGui import (
from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.error import logException
-from novelwriter.common import NWConfigParser, minmax, readTextFile
+from novelwriter.common import NWConfigParser, minmax
from novelwriter.constants import nwLabels
logger = logging.getLogger(__name__)
@@ -67,6 +67,7 @@ class GuiTheme:
self.themeUrl = ""
self.themeLicense = ""
self.themeLicenseUrl = ""
+ self.themeIcons = ""
# GUI
self.statNone = [120, 120, 120]
@@ -104,16 +105,14 @@ class GuiTheme:
self.colRepTag = [0, 0, 0]
self.colMod = [0, 0, 0]
- # Changeable Settings
- self.guiTheme = None
- self.guiSyntax = None
- self.syntaxFile = None
- self.cssFile = None
- self.guiFontDB = QFontDatabase()
-
# Class Setup
# ===========
+ # Init GUI Font
+ self.guiFontDB = QFontDatabase()
+ self._setGuiFont()
+
+ # Load Themes
self._guiPalette = QPalette()
self._themeList = []
self._syntaxList = []
@@ -127,9 +126,8 @@ class GuiTheme:
self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax"))
self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes"))
- self.updateFont()
- self.updateTheme()
- self.iconCache.updateTheme()
+ self.loadTheme()
+ self.loadSyntax()
# Icon Functions
self.getIcon = self.iconCache.getIcon
@@ -183,77 +181,26 @@ class GuiTheme:
return int(ceil(qMetrics.boundingRect(theText).width()))
##
- # Actions
+ # Theme Methods
##
- def updateFont(self):
- """Update the GUI's font style from settings.
- """
- theFont = QFont()
- if self.mainConf.guiFont not in self.guiFontDB.families():
- if self.mainConf.osWindows and "Arial" in self.guiFontDB.families():
- # On Windows we default to Arial if possible
- theFont.setFamily("Arial")
- theFont.setPointSize(10)
- else:
- theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
- self.mainConf.guiFont = theFont.family()
- self.mainConf.guiFontSize = theFont.pointSize()
- else:
- theFont.setFamily(self.mainConf.guiFont)
- theFont.setPointSize(self.mainConf.guiFontSize)
-
- qApp.setFont(theFont)
-
- return
-
- def updateTheme(self):
- """Update the GUI theme from theme files.
- """
- self.guiTheme = self.mainConf.guiTheme
- self.guiSyntax = self.mainConf.guiSyntax
-
- self.themeFile = self._availThemes.get(self.guiTheme, None)
- if self.themeFile is None:
- logger.error("Could not find GUI theme '%s'", self.guiTheme)
- else:
- self.cssFile = self.themeFile[:-5]+".css"
- self.loadTheme()
-
- self.syntaxFile = self._availSyntax.get(self.guiSyntax, None)
- if self.syntaxFile is None:
- logger.error("Could not find syntax theme '%s'", self.guiSyntax)
- else:
- self.loadSyntax()
-
- # Update dependant colours
- backCol = qApp.palette().window().color()
- textCol = qApp.palette().windowText().color()
-
- backLCol = backCol.lightnessF()
- textLCol = textCol.lightnessF()
-
- if backLCol > textLCol:
- helpLCol = textLCol + 0.65*(backLCol - textLCol)
- else:
- helpLCol = backLCol + 0.65*(textLCol - backLCol)
-
- self.helpText = [int(255*helpLCol)]*3
-
- return True
-
def loadTheme(self):
"""Load the currently specified GUI theme.
"""
- logger.info("Loading GUI theme '%s'", self.guiTheme)
+ guiTheme = self.mainConf.guiTheme
+ themeFile = self._availThemes.get(guiTheme, None)
+ if themeFile is None:
+ logger.error("Could not find GUI theme '%s'", guiTheme)
+ return False
# Config File
+ logger.info("Loading GUI theme '%s'", guiTheme)
confParser = NWConfigParser()
try:
- with open(self.themeFile, mode="r", encoding="utf-8") as inFile:
+ with open(themeFile, mode="r", encoding="utf-8") as inFile:
confParser.read_file(inFile)
except Exception:
- logger.error("Could not load theme settings from: %s", self.themeFile)
+ logger.error("Could not load theme settings from: %s", themeFile)
logException()
return False
@@ -267,6 +214,7 @@ class GuiTheme:
self.themeUrl = confParser.rdStr(cnfSec, "url", "")
self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
+ self.themeIcons = confParser.rdStr(cnfSec, "icontheme", "")
# Palette
cnfSec = "Palette"
@@ -285,18 +233,32 @@ class GuiTheme:
self._setPalette(confParser, cnfSec, "highlightedtext", QPalette.HighlightedText)
self._setPalette(confParser, cnfSec, "link", QPalette.Link)
self._setPalette(confParser, cnfSec, "linkvisited", QPalette.LinkVisited)
+ else:
+ self._guiPalette = qApp.style().standardPalette()
# GUI
cnfSec = "GUI"
if confParser.has_section(cnfSec):
- self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
- self.statUnsaved = self._loadColour(confParser, cnfSec, "statusunsaved")
- self.statSaved = self._loadColour(confParser, cnfSec, "statussaved")
+ self.statNone = self._parseColour(confParser, cnfSec, "statusnone")
+ self.statUnsaved = self._parseColour(confParser, cnfSec, "statusunsaved")
+ self.statSaved = self._parseColour(confParser, cnfSec, "statussaved")
- # CSS File
- cssData = readTextFile(self.cssFile)
- if cssData:
- qApp.setStyleSheet(cssData)
+ # Icons
+ self.iconCache.loadTheme(self.themeIcons)
+
+ # Update Dependant Colours
+ backCol = self._guiPalette.window().color()
+ textCol = self._guiPalette.windowText().color()
+
+ backLCol = backCol.lightnessF()
+ textLCol = textCol.lightnessF()
+
+ if backLCol > textLCol:
+ helpLCol = textLCol + 0.65*(backLCol - textLCol)
+ else:
+ helpLCol = backLCol + 0.65*(textLCol - backLCol)
+
+ self.helpText = [int(255*helpLCol)]*3
# Apply Styles
qApp.setPalette(self._guiPalette)
@@ -306,14 +268,20 @@ class GuiTheme:
def loadSyntax(self):
"""Load the currently specified syntax highlighter theme.
"""
- logger.info("Loading syntax theme '%s'", self.guiSyntax)
+ guiSyntax = self.mainConf.guiSyntax
+ syntaxFile = self._availSyntax.get(guiSyntax, None)
+ if syntaxFile is None:
+ logger.error("Could not find syntax theme '%s'", guiSyntax)
+ return False
+
+ logger.info("Loading syntax theme '%s'", guiSyntax)
confParser = NWConfigParser()
try:
- with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile:
+ with open(syntaxFile, mode="r", encoding="utf-8") as inFile:
confParser.read_file(inFile)
except Exception:
- logger.error("Could not load syntax colours from: %s", self.syntaxFile)
+ logger.error("Could not load syntax colours from: %s", syntaxFile)
logException()
return False
@@ -321,32 +289,32 @@ class GuiTheme:
cnfSec = "Main"
if confParser.has_section(cnfSec):
self.syntaxName = confParser.rdStr(cnfSec, "name", "")
- self.syntaxDescription = confParser.rdStr(cnfSec, "description", "")
- self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "")
- self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "")
+ self.syntaxDescription = confParser.rdStr(cnfSec, "description", "N/A")
+ self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "N/A")
+ self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "N/A")
self.syntaxUrl = confParser.rdStr(cnfSec, "url", "")
- self.syntaxLicense = confParser.rdStr(cnfSec, "license", "")
+ self.syntaxLicense = confParser.rdStr(cnfSec, "license", "N/A")
self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
# Syntax
cnfSec = "Syntax"
if confParser.has_section(cnfSec):
- self.colBack = self._loadColour(confParser, cnfSec, "background")
- self.colText = self._loadColour(confParser, cnfSec, "text")
- self.colLink = self._loadColour(confParser, cnfSec, "link")
- self.colHead = self._loadColour(confParser, cnfSec, "headertext")
- self.colHeadH = self._loadColour(confParser, cnfSec, "headertag")
- self.colEmph = self._loadColour(confParser, cnfSec, "emphasis")
- self.colDialN = self._loadColour(confParser, cnfSec, "straightquotes")
- self.colDialD = self._loadColour(confParser, cnfSec, "doublequotes")
- self.colDialS = self._loadColour(confParser, cnfSec, "singlequotes")
- self.colHidden = self._loadColour(confParser, cnfSec, "hidden")
- self.colKey = self._loadColour(confParser, cnfSec, "keyword")
- self.colVal = self._loadColour(confParser, cnfSec, "value")
- self.colSpell = self._loadColour(confParser, cnfSec, "spellcheckline")
- self.colError = self._loadColour(confParser, cnfSec, "errorline")
- self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag")
- self.colMod = self._loadColour(confParser, cnfSec, "modifier")
+ self.colBack = self._parseColour(confParser, cnfSec, "background")
+ self.colText = self._parseColour(confParser, cnfSec, "text")
+ self.colLink = self._parseColour(confParser, cnfSec, "link")
+ self.colHead = self._parseColour(confParser, cnfSec, "headertext")
+ self.colHeadH = self._parseColour(confParser, cnfSec, "headertag")
+ self.colEmph = self._parseColour(confParser, cnfSec, "emphasis")
+ self.colDialN = self._parseColour(confParser, cnfSec, "straightquotes")
+ self.colDialD = self._parseColour(confParser, cnfSec, "doublequotes")
+ self.colDialS = self._parseColour(confParser, cnfSec, "singlequotes")
+ self.colHidden = self._parseColour(confParser, cnfSec, "hidden")
+ self.colKey = self._parseColour(confParser, cnfSec, "keyword")
+ self.colVal = self._parseColour(confParser, cnfSec, "value")
+ self.colSpell = self._parseColour(confParser, cnfSec, "spellcheckline")
+ self.colError = self._parseColour(confParser, cnfSec, "errorline")
+ self.colRepTag = self._parseColour(confParser, cnfSec, "replacetag")
+ self.colMod = self._parseColour(confParser, cnfSec, "modifier")
return True
@@ -388,52 +356,64 @@ class GuiTheme:
# Internal Functions
##
+ def _setGuiFont(self):
+ """Update the GUI's font style from settings.
+ """
+ theFont = QFont()
+ if self.mainConf.guiFont not in self.guiFontDB.families():
+ if self.mainConf.osWindows and "Arial" in self.guiFontDB.families():
+ # On Windows we default to Arial if possible
+ theFont.setFamily("Arial")
+ theFont.setPointSize(10)
+ else:
+ theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
+ self.mainConf.guiFont = theFont.family()
+ self.mainConf.guiFontSize = theFont.pointSize()
+ else:
+ theFont.setFamily(self.mainConf.guiFont)
+ theFont.setPointSize(self.mainConf.guiFontSize)
+
+ qApp.setFont(theFont)
+
+ return
+
def _listConf(self, targetDict, checkDir):
- """Scan for syntax and gui themes and populate the dictionary.
+ """Scan for theme config files and populate the dictionary.
"""
if not os.path.isdir(checkDir):
- return
+ return False
for checkFile in os.listdir(checkDir):
confPath = os.path.join(checkDir, checkFile)
if os.path.isfile(confPath) and confPath.endswith(".conf"):
targetDict[checkFile[:-5]] = confPath
- return
+ return True
- def _loadColour(self, confParser, cnfSec, cnfName):
- """Load a colour value from a config string.
+ def _parseColour(self, confParser, cnfSec, cnfName):
+ """Parse a colour value from a config string.
"""
if confParser.has_option(cnfSec, cnfName):
- inData = confParser.get(cnfSec, cnfName).split(",")
- outData = []
+ values = confParser.get(cnfSec, cnfName).split(",")
+ result = []
try:
- outData.append(int(inData[0]))
- outData.append(int(inData[1]))
- outData.append(int(inData[2]))
+ result.append(minmax(int(values[0]), 0, 255))
+ result.append(minmax(int(values[1]), 0, 255))
+ result.append(minmax(int(values[2]), 0, 255))
except Exception:
logger.error("Could not load theme colours for '%s' from config file", cnfName)
- outData = [0, 0, 0]
+ result = [0, 0, 0]
else:
logger.warning("Could not find theme colours for '%s' in config file", cnfName)
- outData = [0, 0, 0]
- return outData
+ result = [0, 0, 0]
+ return result
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
"""Set a palette colour value from a config string.
"""
- readCol = []
- if confParser.has_option(cnfSec, cnfName):
- inData = confParser.get(cnfSec, cnfName).split(",")
- try:
- readCol.append(int(inData[0]))
- readCol.append(int(inData[1]))
- readCol.append(int(inData[2]))
- except Exception:
- logger.error("Could not load theme colours for '%s' from config file", cnfName)
- return
- if len(readCol) == 3:
- self._guiPalette.setColor(paletteVal, QColor(*readCol))
+ self._guiPalette.setColor(
+ paletteVal, QColor(*self._parseColour(confParser, cnfSec, cnfName))
+ )
return
# End Class GuiTheme
@@ -492,13 +472,11 @@ class GuiIcons:
# Storage
self._qIcons = {}
self._themeMap = {}
- self._themeList = []
self._headerDec = []
self._confName = "icons.conf"
# Icon Theme Path
- self._iconPath = os.path.join(self.mainConf.assetPath, "icons")
- self._themePath = os.path.join(self._iconPath, "system")
+ self._iconPath = os.path.join(self.mainConf.assetPath, "icons")
# Icon Theme Meta
self.themeName = ""
@@ -515,20 +493,19 @@ class GuiIcons:
# Actions
##
- def updateTheme(self):
+ def loadTheme(self, iconTheme):
"""Update the theme map. This is more of an init, since many of
the GUI icons cannot really be replaced without writing specific
update functions for the classes where they're used.
"""
self._themeMap = {}
- themePath = self._getThemePath()
- if themePath is None:
- logger.warning("No icons loaded")
+ themePath = os.path.join(self.mainConf.assetPath, "icons", iconTheme)
+ if not os.path.isdir(themePath):
+ logger.warning("No icons loaded for '%s'", iconTheme)
return False
- self._themePath = themePath
themeConf = os.path.join(themePath, self._confName)
- logger.info("Loading icon theme '%s'", self.mainConf.guiIcons)
+ logger.info("Loading icon theme '%s'", iconTheme)
# Config File
confParser = NWConfigParser()
@@ -558,7 +535,7 @@ class GuiIcons:
if iconName not in self.ICON_KEYS:
logger.error("Unknown icon name '%s' in config file", iconName)
else:
- iconPath = os.path.join(self._themePath, iconFile)
+ iconPath = os.path.join(themePath, iconFile)
if os.path.isfile(iconPath):
self._themeMap[iconName] = iconPath
logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile)
@@ -574,6 +551,14 @@ class GuiIcons:
if iconKey not in self._themeMap:
logger.error("No icon file specified for '%s'", iconKey)
+ # Refresh icons
+ for iconKey in self._qIcons:
+ logger.debug("Reloading icon: '%s'", iconKey)
+ qIcon = self._loadIcon(iconKey)
+ self._qIcons[iconKey] = qIcon
+
+ self._headerDec = []
+
return True
##
@@ -595,7 +580,7 @@ class GuiIcons:
return QPixmap()
if not os.path.isfile(imgPath):
- logger.error("Asset '%s' not found", self.IMAGE_MAP[decoKey])
+ logger.error("Asset not found: %s", imgPath)
return QPixmap()
theDeco = QPixmap(imgPath)
@@ -608,7 +593,7 @@ class GuiIcons:
return theDeco
- def getIcon(self, iconKey, iconSize=None):
+ def getIcon(self, iconKey):
"""Return an icon from the icon buffer. If it doesn't exist,
return, load it, and if it still doesn't exist, return an empty
icon.
@@ -668,49 +653,10 @@ class GuiIcons:
]
return self._headerDec[minmax(hLevel, 0, 4)]
- def listThemes(self):
- """Scan the icons themes folder and list all themes.
- """
- if self._themeList:
- return self._themeList
-
- confParser = NWConfigParser()
- for themeDir in os.listdir(self._iconPath):
- themePath = os.path.join(self._iconPath, themeDir)
- if not os.path.isdir(themePath):
- continue
-
- logger.debug("Checking icon theme config for '%s'", themeDir)
- themeConf = os.path.join(themePath, self._confName)
- themeName = _loadInternalName(confParser, themeConf)
- if themeName:
- self._themeList.append((themeDir, themeName))
-
- self._themeList = sorted(self._themeList, key=lambda x: x[1])
-
- return self._themeList
-
##
# Internal Functions
##
- def _getThemePath(self):
- """Get a valid theme path. Returns None if it fails.
- """
- themePath = os.path.join(self.mainConf.assetPath, "icons", self.mainConf.guiIcons)
- if not os.path.isdir(themePath):
- logger.warning(
- "Icon theme '%s' not found, resetting to default", self.mainConf.guiIcons
- )
- self.mainConf.setDefaultIconTheme()
-
- themePath = os.path.join(self.mainConf.assetPath, "icons", self.mainConf.guiIcons)
- if not os.path.isdir(themePath):
- logger.error("Default icon theme not found")
- return None
-
- return themePath
-
def _loadIcon(self, iconKey):
"""Load an icon from the assets themes folder. Is guaranteed to
return a QIcon.
diff --git a/novelwriter/gui/viewsbar.py b/novelwriter/gui/viewsbar.py
index dcd697db..da85275c 100644
--- a/novelwriter/gui/viewsbar.py
+++ b/novelwriter/gui/viewsbar.py
@@ -61,7 +61,6 @@ class GuiViewsBar(QToolBar):
self.setIconSize(QSize(iPx, iPx))
self.setMaximumWidth(mPx)
self.setContentsMargins(0, 0, 0, 0)
- self.setStyleSheet("QToolBar {border: 0px;}")
stretch = QWidget(self)
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -70,37 +69,31 @@ class GuiViewsBar(QToolBar):
self.aProject = QAction(self.tr("Project"), self)
self.aProject.setFont(lblFont)
self.aProject.setToolTip(self.tr("Project Tree View"))
- self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
self.aNovel = QAction(self.tr("Novel"), self)
self.aNovel.setFont(lblFont)
self.aNovel.setToolTip(self.tr("Novel Tree View"))
- self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
self.aOutline = QAction(self.tr("Outline"), self)
self.aOutline.setFont(lblFont)
self.aOutline.setToolTip(self.tr("Novel Outline View"))
- self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
self.aBuild = QAction(self.tr("Build"), self)
self.aBuild.setFont(lblFont)
self.aBuild.setToolTip(self.tr("Build Novel Project"))
- self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
self.aDetails = QAction(self.tr("Details"), self)
self.aDetails.setFont(lblFont)
self.aDetails.setToolTip(self.tr("Project Details"))
- self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
self.aStats = QAction(self.tr("Stats"), self)
self.aStats.setFont(lblFont)
self.aStats.setToolTip(self.tr("Writing Statistics"))
- self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
# Settings Menu
@@ -114,7 +107,6 @@ class GuiViewsBar(QToolBar):
self.tbSettings = QToolButton(self)
self.tbSettings.setFont(lblFont)
self.tbSettings.setText(self.tr("Settings"))
- self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
self.tbSettings.setMenu(self.mSettings)
self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.tbSettings.setPopupMode(QToolButton.InstantPopup)
@@ -129,8 +121,25 @@ class GuiViewsBar(QToolBar):
self.addAction(self.aStats)
self.addWidget(self.tbSettings)
+ self.updateTheme()
+
logger.debug("GuiViewsBar initialisation complete")
return
+ def updateTheme(self):
+ """Initialise GUI elements that depend on specific settings.
+ """
+ self.setStyleSheet("QToolBar {border: 0px;}")
+
+ self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
+ self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
+ self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
+ self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
+ self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
+ self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
+ self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
+
+ return
+
# END Class GuiViewsBar
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index cf0a9dff..58c1e72a 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -907,13 +907,39 @@ class GuiMain(QMainWindow):
if dlgConf.result() == QDialog.Accepted:
logger.debug("Applying new preferences")
self.initMain()
- self.mainTheme.updateTheme()
self.saveDocument()
+
+ if dlgConf.needsRestart:
+ self.makeAlert(self.tr(
+ "Some changes will not be applied until novelWriter has been restarted."
+ ), nwAlert.INFO)
+
+ if dlgConf.refreshTree:
+ self.projView.populateTree()
+
+ if dlgConf.updateTheme:
+ # We are doing this manually instead of connecting to
+ # qApp.paletteChanged since the processing order matters
+ self.mainTheme.loadTheme()
+ self.docEditor.updateTheme()
+ self.docViewer.updateTheme()
+ self.viewsBar.updateTheme()
+ self.projView.updateTheme()
+ self.novelView.updateTheme()
+ self.outlineView.updateTheme()
+ self.itemDetails.updateTheme()
+ self.mainStatus.updateTheme()
+
+ if dlgConf.updateSyntax:
+ self.mainTheme.loadSyntax()
+ self.docEditor.updateSyntaxColours()
+
self.docEditor.initEditor()
self.docViewer.initViewer()
self.projView.initSettings()
self.novelView.initSettings()
self.outlineView.initSettings()
+
self._updateStatusWordCount()
return
diff --git a/tests/conftest.py b/tests/conftest.py
index 40725923..12842add 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -162,7 +162,11 @@ def mockGUI(monkeypatch, tmpConf):
def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
"""Create an instance of the novelWriter GUI.
"""
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
+ monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Ok)
+ monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
+ monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Ok)
+ monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
+
monkeypatch.setattr("novelwriter.CONFIG", fncConf)
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
qtbot.addWidget(nwGUI)
diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf
index 56b5a807..87c65cba 100644
--- a/tests/reference/baseConfig_novelwriter.conf
+++ b/tests/reference/baseConfig_novelwriter.conf
@@ -1,8 +1,7 @@
[Main]
-timestamp = 2021-12-31 16:45:32
+timestamp = 2022-10-26 11:19:49
theme = default
syntax = default_light
-icons = typicons_light
guifont =
guifontsize = 11
lastnotes = 0x0
diff --git a/tests/reference/guiPreferences_novelwriter.conf b/tests/reference/guiPreferences_novelwriter.conf
index 9d21830d..d42234e8 100644
--- a/tests/reference/guiPreferences_novelwriter.conf
+++ b/tests/reference/guiPreferences_novelwriter.conf
@@ -1,9 +1,8 @@
[Main]
-timestamp = 2021-12-31 16:45:34
+timestamp = 2022-10-26 11:19:51
theme = default
syntax = default_light
-icons = typicons_light
-guifont = Sans
+guifont = Cantarell
guifontsize = 12
lastnotes = 0x0
guilang = en_GB
@@ -12,7 +11,7 @@ hidehscroll = True
[Sizes]
geometry = 1200, 650
-preferences = 670, 589
+preferences = 699, 614
projcols = 200, 60, 140
mainpane = 300, 800
docpane = 400, 400
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index f6721493..61994f04 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -192,32 +192,6 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
tstConf.doReplaceSQuote = orDoSng
assert tstConf.saveConfig() is True
- # Test Correcting icon theme
- origIcons = tstConf.guiIcons
-
- tstConf.guiIcons = "typicons_colour_dark"
- assert tstConf.saveConfig() is True
- assert tstConf.loadConfig() is True
- assert tstConf.guiIcons == "typicons_dark"
-
- tstConf.guiIcons = "typicons_grey_dark"
- assert tstConf.saveConfig() is True
- assert tstConf.loadConfig() is True
- assert tstConf.guiIcons == "typicons_dark"
-
- tstConf.guiIcons = "typicons_colour_light"
- assert tstConf.saveConfig() is True
- assert tstConf.loadConfig() is True
- assert tstConf.guiIcons == "typicons_light"
-
- tstConf.guiIcons = "typicons_grey_light"
- assert tstConf.saveConfig() is True
- assert tstConf.loadConfig() is True
- assert tstConf.guiIcons == "typicons_light"
-
- tstConf.guiIcons = origIcons
- assert tstConf.saveConfig()
-
# Localisation
# ============
diff --git a/tests/test_dialogs/test_dlg_about.py b/tests/test_dialogs/test_dlg_about.py
index 80a6ca06..63de9039 100644
--- a/tests/test_dialogs/test_dlg_about.py
+++ b/tests/test_dialogs/test_dlg_about.py
@@ -29,12 +29,9 @@ from novelwriter.dialogs.about import GuiAbout
@pytest.mark.gui
-def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
+def testDlgAbout_NWDialog(qtbot, nwGUI):
"""Test the novelWriter about dialogs.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
# NW About
nwGUI.mainTheme.themeName = "A Theme"
nwGUI.mainTheme.themeAuthor = "An Author"
@@ -74,8 +71,6 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
def testDlgAbout_QtDialog(monkeypatch, nwGUI):
"""Test the Qt about dialogs.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *a, **k: None)
# Open About
diff --git a/tests/test_dialogs/test_dlg_dialogs.py b/tests/test_dialogs/test_dlg_dialogs.py
index 9afcf410..772554ea 100644
--- a/tests/test_dialogs/test_dlg_dialogs.py
+++ b/tests/test_dialogs/test_dlg_dialogs.py
@@ -22,7 +22,7 @@ along with this program. If not, see .
import pytest
from PyQt5.QtCore import QItemSelectionModel
-from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox
+from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.dialogs.updates import GuiUpdates
@@ -30,12 +30,9 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
@pytest.mark.gui
-def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
+def testDlgOther_QuoteSelect(qtbot, nwGUI):
"""Test the quote symbols dialog.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
nwQuot = GuiQuoteSelect(nwGUI)
nwQuot.show()
@@ -52,7 +49,7 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
assert nwQuot.result() == QDialog.Accepted
assert nwQuot.selectedQuote == lastItem
- # qtbot.stopForInteraction()
+ # qtbot.stop()
nwQuot._doReject()
nwQuot.close()
@@ -63,9 +60,6 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
"""Test the check for updates dialog.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
nwUpdate = GuiUpdates(nwGUI)
nwUpdate.show()
@@ -95,7 +89,7 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
# Trigger from Menu
nwGUI.mainMenu.aUpdates.activate(QAction.Trigger)
- # qtbot.stopForInteraction()
+ # qtbot.stop()
nwUpdate._doClose()
# END Test testDlgOther_Updates
diff --git a/tests/test_dialogs/test_dlg_docmerge.py b/tests/test_dialogs/test_dlg_docmerge.py
index 4b86fa78..1cf4768c 100644
--- a/tests/test_dialogs/test_dlg_docmerge.py
+++ b/tests/test_dialogs/test_dlg_docmerge.py
@@ -24,19 +24,14 @@ import pytest
from tools import buildTestProject, C
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QMessageBox
from novelwriter.dialogs.docmerge import GuiDocMerge
@pytest.mark.gui
-def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
+def testDlgMerge_Main(qtbot, nwGUI, fncProj, mockRnd):
"""Test the merge documents tool.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
-
# Create a new project
buildTestProject(nwGUI, fncProj)
diff --git a/tests/test_dialogs/test_dlg_docsplit.py b/tests/test_dialogs/test_dlg_docsplit.py
index 4689f76d..05092c67 100644
--- a/tests/test_dialogs/test_dlg_docsplit.py
+++ b/tests/test_dialogs/test_dlg_docsplit.py
@@ -23,8 +23,6 @@ import pytest
from tools import C, buildTestProject
-from PyQt5.QtWidgets import QMessageBox
-
from novelwriter.dialogs.docsplit import GuiDocSplit
from novelwriter.dialogs.editlabel import GuiEditLabel
@@ -33,9 +31,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the split document tool.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create a new project
diff --git a/tests/test_dialogs/test_dlg_preferences.py b/tests/test_dialogs/test_dlg_preferences.py
index 22461751..17dd6246 100644
--- a/tests/test_dialogs/test_dlg_preferences.py
+++ b/tests/test_dialogs/test_dlg_preferences.py
@@ -35,9 +35,7 @@ from novelwriter.config import Config
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.dialogs.preferences import GuiPreferences
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
+KEY_DELAY = 1
@pytest.mark.gui
@@ -63,7 +61,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
qtbot.addWidget(nwGUI)
nwGUI.show()
- qtbot.wait(stepDelay)
theConf = nwGUI.mainConf
assert theConf.confPath == fncDir
@@ -72,30 +69,40 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
- nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
- qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
+ with monkeypatch.context() as mp:
+ mp.setattr(GuiPreferences, "updateTheme", lambda *a: True)
+ mp.setattr(GuiPreferences, "updateSyntax", lambda *a: True)
+ mp.setattr(GuiPreferences, "needsRestart", lambda *a: True)
+ mp.setattr(GuiPreferences, "refreshTree", lambda *a: True)
+ nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
+ qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
nwPrefs = getGuiItem("GuiPreferences")
assert isinstance(nwPrefs, GuiPreferences)
nwPrefs.show()
assert nwPrefs.mainConf.confPath == fncDir
+ assert nwPrefs.updateTheme is False
+ assert nwPrefs.updateSyntax is False
+ assert nwPrefs.needsRestart is False
+ assert nwPrefs.refreshTree is False
+
# General Settings
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabGeneral = nwPrefs.tabGeneral
nwPrefs._tabBox.setCurrentWidget(tabGeneral)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert tabGeneral.showFullPath.isChecked()
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
assert not tabGeneral.showFullPath.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabGeneral.hideVScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
assert tabGeneral.hideVScroll.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabGeneral.hideHScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
assert tabGeneral.hideHScroll.isChecked()
@@ -104,21 +111,21 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabGeneral.guiFontSize.setValue(12)
# Projects Settings
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabProjects = nwPrefs.tabProjects
nwPrefs._tabBox.setCurrentWidget(tabProjects)
tabProjects.backupPath = "no/where"
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabProjects.backupOnClose.isChecked()
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
assert tabProjects.backupOnClose.isChecked()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# Check Browse button
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
@@ -126,104 +133,104 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir")
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabProjects.autoSaveDoc.setValue(20)
tabProjects.autoSaveProj.setValue(40)
# Document Settings
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabDocs = nwPrefs.tabDocs
nwPrefs._tabBox.setCurrentWidget(tabDocs)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
qtbot.mouseClick(tabDocs.fontButton, Qt.LeftButton)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabDocs.textSize.setValue(13)
tabDocs.textWidth.setValue(700)
tabDocs.focusWidth.setValue(900)
tabDocs.textMargin.setValue(45)
tabDocs.tabWidth.setValue(45)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabDocs.hideFocusFooter.isChecked()
qtbot.mouseClick(tabDocs.hideFocusFooter, Qt.LeftButton)
assert tabDocs.hideFocusFooter.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabDocs.doJustify.isChecked()
qtbot.mouseClick(tabDocs.doJustify, Qt.LeftButton)
assert tabDocs.doJustify.isChecked()
# Editor Settings
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabEditor = nwPrefs.tabEditor
nwPrefs._tabBox.setCurrentWidget(tabEditor)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabEditor.showTabsNSpaces.isChecked()
qtbot.mouseClick(tabEditor.showTabsNSpaces, Qt.LeftButton)
assert tabEditor.showTabsNSpaces.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabEditor.showLineEndings.isChecked()
qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton)
assert tabEditor.showLineEndings.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabEditor.autoScroll.isChecked()
qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton)
assert tabEditor.autoScroll.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabEditor.scrollPastEnd.setValue(0)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabEditor.bigDocLimit.setValue(500)
# Syntax Settings
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabSyntax = nwPrefs.tabSyntax
nwPrefs._tabBox.setCurrentWidget(tabSyntax)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert tabSyntax.highlightQuotes.isChecked()
qtbot.mouseClick(tabSyntax.highlightQuotes, Qt.LeftButton)
assert not tabSyntax.highlightQuotes.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert tabSyntax.highlightEmph.isChecked()
qtbot.mouseClick(tabSyntax.highlightEmph, Qt.LeftButton)
assert not tabSyntax.highlightEmph.isChecked()
# Automation Settings
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabAuto = nwPrefs.tabAuto
nwPrefs._tabBox.setCurrentWidget(tabAuto)
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert tabAuto.autoSelect.isChecked()
qtbot.mouseClick(tabAuto.autoSelect, Qt.LeftButton)
assert not tabAuto.autoSelect.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert tabAuto.doReplace.isChecked()
qtbot.mouseClick(tabAuto.doReplace, Qt.LeftButton)
assert not tabAuto.doReplace.isChecked()
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
assert not tabAuto.doReplaceSQuote.isEnabled()
assert not tabAuto.doReplaceDQuote.isEnabled()
assert not tabAuto.doReplaceDash.isEnabled()
assert not tabAuto.doReplaceDots.isEnabled()
# Quotation Style
- qtbot.wait(keyDelay)
+ qtbot.wait(KEY_DELAY)
tabQuote = nwPrefs.tabQuote
nwPrefs._tabBox.setCurrentWidget(tabQuote)
monkeypatch.setattr(GuiQuoteSelect, "selectedQuote", "'")
- monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *args: QDialog.Accepted)
+ monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *a: QDialog.Accepted)
qtbot.mouseClick(tabQuote.btnDoubleStyleC, Qt.LeftButton)
# Save and Check Config
@@ -249,6 +256,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
novelwriter.CONFIG = origConf
nwGUI.closeMain()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testDlgPreferences_Main
diff --git a/tests/test_dialogs/test_dlg_projdetails.py b/tests/test_dialogs/test_dlg_projdetails.py
index b2952ad6..84360d11 100644
--- a/tests/test_dialogs/test_dlg_projdetails.py
+++ b/tests/test_dialogs/test_dlg_projdetails.py
@@ -23,25 +23,15 @@ import pytest
from tools import getGuiItem
-from PyQt5.QtWidgets import QAction, QMessageBox
+from PyQt5.QtWidgets import QAction
from novelwriter.dialogs.projdetails import GuiProjectDetails
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
-def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
+def testDlgProjDetails_Dialog(qtbot, nwGUI, nwLipsum):
"""Test the project details dialog.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Create a project to work on
assert nwGUI.openProject(nwLipsum)
assert nwGUI.rebuildIndex(beQuiet=True)
@@ -54,7 +44,6 @@ def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
projDet = getGuiItem("GuiProjectDetails")
assert isinstance(projDet, GuiProjectDetails)
- qtbot.wait(stepDelay)
# Overview Page
# =============
@@ -105,7 +94,7 @@ def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i]
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i]
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# Clean Up
projDet._doClose()
diff --git a/tests/test_dialogs/test_dlg_projload.py b/tests/test_dialogs/test_dlg_projload.py
index 5916d185..6e81ffe2 100644
--- a/tests/test_dialogs/test_dlg_projload.py
+++ b/tests/test_dialogs/test_dlg_projload.py
@@ -26,29 +26,19 @@ from tools import getGuiItem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
- QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog,
- QMessageBox
+ QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog
)
from novelwriter.dialogs.projload import GuiProjectLoad
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the load project wizard.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
assert nwGUI.openProject(nwMinimal)
assert nwGUI.closeProject()
- qtbot.wait(stepDelay)
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
@@ -58,22 +48,18 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
- qtbot.wait(stepDelay)
recentCount = nwLoad.listBox.topLevelItemCount()
assert recentCount > 0
- qtbot.wait(stepDelay)
selItem = nwLoad.listBox.topLevelItem(0)
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
assert isinstance(selItem, QTreeWidgetItem)
- qtbot.wait(stepDelay)
nwLoad.selPath.setText("")
nwLoad.listBox.setCurrentItem(selItem)
nwLoad._doSelectRecent()
assert nwLoad.selPath.text() == selPath
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
assert nwLoad.openPath == selPath
assert nwLoad.openState == nwLoad.OPEN_STATE
@@ -81,27 +67,22 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
# Just create a new project load from scratch for the rest of the test
del nwLoad
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
- qtbot.wait(stepDelay)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NONE_STATE
- qtbot.wait(stepDelay)
nwLoad.show()
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NEW_STATE
- qtbot.wait(stepDelay)
nwLoad.show()
nwLoad._doDeleteRecent()
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
@@ -113,6 +94,6 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
assert nwLoad.openState == nwLoad.OPEN_STATE
nwLoad.close()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testDlgLoadProject_Main
diff --git a/tests/test_dialogs/test_dlg_projsettings.py b/tests/test_dialogs/test_dlg_projsettings.py
index 48e896bf..bb38f317 100644
--- a/tests/test_dialogs/test_dlg_projsettings.py
+++ b/tests/test_dialogs/test_dlg_projsettings.py
@@ -26,14 +26,12 @@ from tools import C, getGuiItem, buildTestProject
from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog
+from PyQt5.QtWidgets import QDialog, QAction, QColorDialog
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projsettings import GuiProjectSettings
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
+KEY_DELAY = 1
@pytest.mark.gui
@@ -41,10 +39,6 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
"""Test the main dialog class. Saving settings is not tested in this
test, but are instead tested in the individual tab tests.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Block the GUI blocking thread
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
@@ -91,10 +85,6 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the main tab of the project settings dialog.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Mock components
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
@@ -125,23 +115,21 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd
assert tabMain.spellLang.currentData() == "en"
assert tabMain.doBackup.isChecked() is False
- qtbot.wait(stepDelay)
tabMain.editName.setText("")
for c in "Project Name":
- qtbot.keyClick(tabMain.editName, c, delay=typeDelay)
+ qtbot.keyClick(tabMain.editName, c, delay=KEY_DELAY)
tabMain.editTitle.setText("")
for c in "Project Title":
- qtbot.keyClick(tabMain.editTitle, c, delay=typeDelay)
+ qtbot.keyClick(tabMain.editTitle, c, delay=KEY_DELAY)
tabMain.editAuthors.clear()
for c in "Jane Doe":
- qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay)
- qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(tabMain.editAuthors, c, delay=KEY_DELAY)
+ qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=KEY_DELAY)
for c in "John Doh":
- qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay)
- qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(tabMain.editAuthors, c, delay=KEY_DELAY)
+ qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=KEY_DELAY)
- qtbot.wait(stepDelay)
assert tabMain.editName.text() == "Project Name"
assert tabMain.editTitle.text() == "Project Title"
assert tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
@@ -164,9 +152,6 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
"""Test the status and importance tabs of the project settings
dialog.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Mock components
@@ -230,9 +215,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
for _ in range(8):
- qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=typeDelay)
+ qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=KEY_DELAY)
for c in "Final":
- qtbot.keyClick(tabStatus.editName, c, delay=typeDelay)
+ qtbot.keyClick(tabStatus.editName, c, delay=KEY_DELAY)
qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
assert tabStatus.listBox.topLevelItemCount() == 4
@@ -310,9 +295,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
tabImport.listBox.clearSelection()
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3))
for _ in range(8):
- qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=typeDelay)
+ qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=KEY_DELAY)
for c in "Final":
- qtbot.keyClick(tabImport.editName, c, delay=typeDelay)
+ qtbot.keyClick(tabImport.editName, c, delay=KEY_DELAY)
qtbot.mouseClick(tabImport.colButton, Qt.LeftButton)
qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton)
assert tabImport.listBox.topLevelItemCount() == 4
@@ -368,9 +353,6 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the auto-replace tab of the project settings dialog.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Mock components
@@ -419,10 +401,10 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mock
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
tabReplace.editKey.setText("")
for c in "Th is ":
- qtbot.keyClick(tabReplace.editKey, c, delay=typeDelay)
+ qtbot.keyClick(tabReplace.editKey, c, delay=KEY_DELAY)
tabReplace.editValue.setText("")
for c in "With This Stuff ":
- qtbot.keyClick(tabReplace.editValue, c, delay=typeDelay)
+ qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY)
qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
assert tabReplace.listBox.topLevelItem(2).text(0) == ""
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff "
diff --git a/tests/test_dialogs/test_dlg_wordlist.py b/tests/test_dialogs/test_dlg_wordlist.py
index 994fb407..24d61157 100644
--- a/tests/test_dialogs/test_dlg_wordlist.py
+++ b/tests/test_dialogs/test_dlg_wordlist.py
@@ -23,7 +23,7 @@ import os
import pytest
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QDialog, QMessageBox, QAction
+from PyQt5.QtWidgets import QDialog, QAction
from tools import writeFile, readFile, getGuiItem
from mock import causeOSError
@@ -31,25 +31,17 @@ from mock import causeOSError
from novelwriter.constants import nwFiles
from novelwriter.dialogs.wordlist import GuiWordList
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
"""test the word list editor.
"""
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
# Open project
nwGUI.openProject(nwMinimal)
- qtbot.wait(stepDelay)
dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
# Load the dialog
@@ -59,7 +51,6 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
wList = getGuiItem("GuiWordList")
assert isinstance(wList, GuiWordList)
wList.show()
- qtbot.wait(stepDelay)
# List should be blank
assert wList.listBox.count() == 0
@@ -73,7 +64,6 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
"word_f\n"
"word_b\n"
))
- qtbot.wait(stepDelay)
assert wList._loadWordList()
# Check that the content was loaded
@@ -130,7 +120,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
monkeypatch.setattr("builtins.open", causeOSError)
assert not wList._doSave()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
wList._doClose()
# END Test testDlgWordList_Dialog
diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py
index 68f02c71..e2ba77ae 100644
--- a/tests/test_gui/test_gui_doceditor.py
+++ b/tests/test_gui/test_gui_doceditor.py
@@ -25,33 +25,26 @@ from mock import causeOSError
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
-from PyQt5.QtWidgets import QAction, QMessageBox, qApp
+from PyQt5.QtWidgets import QAction, qApp
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout
from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.core.index import countWords
from novelwriter.gui.doceditor import GuiDocEditor
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
+KEY_DELAY = 1
@pytest.mark.gui
-def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
+def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
"""Test initialising the editor.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
# Open project
assert nwGUI.openProject(nwMinimal)
assert nwGUI.openDocument("8c659a11cd429")
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
assert nwGUI.saveDocument()
- qtbot.wait(stepDelay)
# Check Defaults
qDoc = nwGUI.docEditor.document()
@@ -80,7 +73,7 @@ def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
assert nwGUI.docEditor.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
assert nwGUI.docEditor._typPadChar == nwUnicode.U_THNBSP
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_Init
@@ -89,10 +82,6 @@ def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
"""Test loading text into the editor.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
@@ -102,7 +91,6 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
nwGUI.docEditor.replaceText(longText)
assert nwGUI.saveDocument() is True
assert nwGUI.closeDocument() is True
- qtbot.wait(stepDelay)
# Load Text
# =========
@@ -142,7 +130,7 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
assert nwGUI.docEditor.loadText(sHandle) is True
assert nwGUI.docEditor.toPlainText() == ""
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_LoadText
@@ -151,15 +139,10 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
"""Test saving text from the editor.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
# Save Text
# =========
@@ -193,24 +176,19 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
# Regular save
assert nwGUI.docEditor.saveText() is True
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_SaveText
@pytest.mark.gui
-def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
+def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
"""Test extracting various meta data and other values.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
# Get Text
# This should replace line and paragraph separators, but preserve
@@ -253,21 +231,16 @@ def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
@pytest.mark.gui
-def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
+def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
"""Test the document actions. This is not an extensive test of the
action features, just that the actions are actually called. The
various action features are tested when their respective functions
are tested.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True
@@ -487,7 +460,7 @@ def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
# Unknown Action
assert nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) is False
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_Actions
@@ -496,15 +469,10 @@ def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test the document insert functions.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True
@@ -577,7 +545,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"\n\n\n", "\n\n@pov: Jane\n@char: John\n\n", 1
)
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_Insert
@@ -586,15 +554,10 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test the text manipulation functions.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True
@@ -791,7 +754,7 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
assert newPara[6] == twoBits[4]
assert newPara[7] == " ".join(twoBits[5:])
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_TextManipulation
@@ -800,15 +763,10 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test the block formatting function.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True
@@ -1115,24 +1073,19 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTex
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_COM) is True
assert nwGUI.docEditor.getText() == "#### Title\n\n% The Text\n\n"
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_BlockFormatting
@pytest.mark.gui
-def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
+def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
"""Test the document editor tags functionality.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Open project
sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
# Create Scene
theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n"
@@ -1181,7 +1134,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
assert nwGUI.closeDocViewer() is True
assert nwGUI.docViewer._docHandle is None
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_Tags
@@ -1190,10 +1143,6 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test saving text from the editor.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
class MockThreadPool:
def __init__(self):
@@ -1226,7 +1175,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count
nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count
assert nwGUI.openDocument(sHandle) is True
- qtbot.wait(stepDelay)
theText = "\n\n".join(ipsumText)
cC, wC, pC = countWords(theText)
@@ -1249,7 +1197,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
nwGUI.docEditor.wCounterDoc.run()
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
- qtbot.wait(stepDelay)
assert nwGUI.theProject.tree[sHandle]._charCount == cC
assert nwGUI.theProject.tree[sHandle]._wordCount == wC
assert nwGUI.theProject.tree[sHandle]._paraCount == pC
@@ -1258,7 +1205,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
# Select all text
assert nwGUI.docEditor.docFooter._docSelection is False
nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
- qtbot.wait(stepDelay)
assert nwGUI.docEditor.docFooter._docSelection is True
# Run the selection word counter
@@ -1267,10 +1213,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
nwGUI.docEditor.wCounterSel.run()
# nwGUI.docEditor._updateSelCounts(cC, wC, pC)
- qtbot.wait(stepDelay)
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected"
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_WordCounters
@@ -1279,14 +1224,11 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the document editor search functionality.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
assert nwGUI.openProject(nwLipsum) is True
assert nwGUI.openDocument("4c4f28287af27") is True
origText = nwGUI.docEditor.getText()
- qtbot.wait(stepDelay)
# Select the Word "est"
nwGUI.docEditor.setCursorPosition(630)
@@ -1301,11 +1243,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Find next by enter key
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
- qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3
# Find next by button
- qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3
# Activate loop search
@@ -1323,14 +1265,14 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docEditor.setCursorPosition(15)
# Toggle search again with header button
- qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.docSearch.setSearchText("")
assert nwGUI.docEditor.docSearch.isVisible() is True
# Search for non-existing
nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
- qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
# Enable RegEx search
@@ -1341,19 +1283,19 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Set invalid RegEx
nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
- qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
# Set dangerous RegEx (issue #1015)
# If this doesn't get caught, the app will hang
nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText(r".*")
- qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 14) < 3
# Set valid RegEx
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
- qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
# Find next and then prev
@@ -1404,7 +1346,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
# Replace "sus" with "foo" via replace button
- qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay)
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.getText()[205:213] == "foocipit"
# Revert last two replaces
@@ -1482,7 +1424,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True)
assert nwGUI.docEditor.focusNextPrevChild(True) is True
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiEditor_Search
diff --git a/tests/test_gui/test_gui_docviewer.py b/tests/test_gui/test_gui_docviewer.py
index ef76d51c..22057e15 100644
--- a/tests/test_gui/test_gui_docviewer.py
+++ b/tests/test_gui/test_gui_docviewer.py
@@ -23,25 +23,17 @@ import pytest
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QTextCursor
-from PyQt5.QtWidgets import qApp, QAction, QMessageBox
+from PyQt5.QtWidgets import qApp, QAction
from mock import causeException
from novelwriter.enum import nwDocAction
from novelwriter.core.tohtml import ToHtml
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the document viewer.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
# Open project
assert nwGUI.openProject(nwLipsum)
@@ -184,6 +176,6 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.loadText("846352075de7d") is False
assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview."
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiViewer_Main
diff --git a/tests/test_gui/test_gui_guimain.py b/tests/test_gui/test_gui_guimain.py
index 5d434601..79fcbe52 100644
--- a/tests/test_gui/test_gui_guimain.py
+++ b/tests/test_gui/test_gui_guimain.py
@@ -37,17 +37,13 @@ from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.gui.noveltree import GuiNovelView
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
+KEY_DELAY = 1
@pytest.mark.gui
-def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
+def testGuiMain_ProjectBlocker(nwGUI):
"""Test the blocking of features when there's no project open.
"""
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
# Test no-project blocking
assert nwGUI.closeProject() is True
assert nwGUI.saveProject() is False
@@ -74,9 +70,6 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
"""Test creating a new project.
"""
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# No data
with monkeypatch.context() as mp:
mp.setattr(GuiProjectWizard, "exec_", lambda *a: None)
@@ -113,8 +106,6 @@ def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test handling of project tree items based on GUI focus states.
"""
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
buildTestProject(nwGUI, fncProj)
sHandle = "000000000000f"
@@ -165,9 +156,6 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd):
"""Test the document editor.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
@@ -195,11 +183,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
- qtbot.wait(stepDelay)
# Re-open project
assert nwGUI.openProject(fncProj)
- qtbot.wait(stepDelay)
# Check that we loaded the data
assert len(nwGUI.theProject.tree) == 8
@@ -241,18 +227,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR)
- qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Jane Doe":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@tag: Jane":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a file about Jane.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Add a Plot File
nwGUI.switchFocus(nwWidget.TREE)
@@ -263,18 +249,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR)
- qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Main Plot":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@tag: MainPlot":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a file detailing the main plot.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Add a World File
nwGUI.switchFocus(nwWidget.TREE)
@@ -290,18 +276,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR)
- qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Main Location":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@tag: Home":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a file describing Jane's home.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Trigger autosaves before making more changes
nwGUI._autoSaveDocument()
@@ -317,67 +303,67 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR)
- qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Novel":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "## Chapter":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@pov: Jane":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@plot: MainPlot":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "### Scene":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "% How about a comment?":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@pov: Jane":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@plot: MainPlot":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@location: Home":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "#### Some Section":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@char: Jane":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a paragraph of nonsense text.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Don't allow Shift+Enter to insert a line separator (issue #1150)
for c in "This is another paragraph":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Enter, modifier=Qt.ShiftModifier, delay=typeDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Enter, modifier=Qt.ShiftModifier, delay=KEY_DELAY)
for c in "with a line separator in it.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Auto-Replace
# ============
@@ -386,43 +372,43 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
"This is another paragraph of much longer nonsense text. "
"It is in fact 1 very very NONSENSICAL nonsense text! "
):
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "Isn't that nice? ":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "Ellipsis? Not a problem either ... ":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "How about three hyphens - -":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=KEY_DELAY)
for c in "- for long dash? It works too.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "\"Full line double quoted text.\"":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "'Full line single quoted text.'":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Insert spaces before and after quotes
nwGUI.mainConf.fmtPadBefore = "\u201d"
nwGUI.mainConf.fmtPadAfter = "\u201c"
for c in "Some \"double quoted text with spaces padded\".":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
nwGUI.mainConf.fmtPadBefore = ""
nwGUI.mainConf.fmtPadAfter = ""
@@ -431,24 +417,24 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
nwGUI.mainConf.fmtPadBefore = ":"
for c in "@object: NoSpaceAdded":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "% synopsis: No space before this colon.":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "Add space before this colon: See?":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "But don't add a double space : See?":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
nwGUI.mainConf.fmtPadBefore = ""
@@ -456,55 +442,49 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# ================
for c in "\t\"Tab-indented text\"":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">\"Paragraph-indented text\"":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">>\"Right-aligned text\"":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "\t'Tab-indented text'":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">'Paragraph-indented text'":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">>'Right-aligned text'":
- qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
- qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
+ qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
+ qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
- qtbot.wait(stepDelay)
nwGUI.docEditor.wCounterDoc.run()
- qtbot.wait(stepDelay)
# Save the document
assert nwGUI.docEditor.docChanged()
assert nwGUI.saveDocument()
assert not nwGUI.docEditor.docChanged()
- qtbot.wait(stepDelay)
nwGUI.rebuildIndex()
- qtbot.wait(stepDelay)
# Open and view the edited document
nwGUI.switchFocus(nwWidget.VIEWER)
assert nwGUI.openDocument(C.hSceneDoc)
assert nwGUI.viewDocument(C.hSceneDoc)
- qtbot.wait(stepDelay)
assert nwGUI.saveProject()
assert nwGUI.closeDocViewer()
- qtbot.wait(stepDelay)
# Check a Quick Create and Delete
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
@@ -553,11 +533,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
@pytest.mark.gui
-def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
+def testGuiMain_FocusFullMode(qtbot, nwGUI, fncProj, mockRnd):
"""Test toggling focus mode in main window.
"""
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
buildTestProject(nwGUI, fncProj)
assert nwGUI.isFocusMode is False
@@ -596,6 +574,6 @@ def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
nwGUI.toggleFullScreenMode()
assert nwGUI.mainConf.isFullScreen is False
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiMain_FocusFullMode
diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py
index c4bf2dd3..e55e1039 100644
--- a/tests/test_gui/test_gui_mainmenu.py
+++ b/tests/test_gui/test_gui_mainmenu.py
@@ -32,24 +32,17 @@ from novelwriter.enum import nwDocAction, nwDocInsert
from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.gui.doceditor import GuiDocEditor
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the main menu Edit and Format entries.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
# Test Document Action with No Project
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False
assert nwGUI.openProject(nwLipsum) is True
- qtbot.wait(stepDelay)
# Split By Chapter
assert nwGUI.openDocument("4c4f28287af27") is True
@@ -61,57 +54,43 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:90] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Italic
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Strikethrough
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:90] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Should get us back to plain
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Double Quotes
nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger)
fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Single Quotes
nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger)
fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Block Formats
# =============
@@ -121,61 +100,50 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger)
fmtStr = "# Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr
- qtbot.wait(stepDelay)
# Header 2
nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger)
fmtStr = "## Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:89] == fmtStr
- qtbot.wait(stepDelay)
# Header 3
nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger)
fmtStr = "### Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:90] == fmtStr
- qtbot.wait(stepDelay)
# Header 4
nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger)
fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:91] == fmtStr
- qtbot.wait(stepDelay)
# Clear Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Comment On
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
fmtStr = "% Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr
- qtbot.wait(stepDelay)
# Comment Off
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Check comment with no space before text
assert nwGUI.docEditor.setCursorPosition(39)
assert nwGUI.docEditor.insertText("%")
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:87] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Undo/Redo
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:87] == fmtStr
- qtbot.wait(stepDelay)
nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText
- qtbot.wait(stepDelay)
# Cut, Copy and Paste
assert nwGUI.docEditor.setCursorPosition(39)
@@ -240,36 +208,30 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger)
fmtStr = "A single, short paragraph. <<"
assert nwGUI.docEditor.getText()[:29] == fmtStr
- qtbot.wait(stepDelay)
# Right Align
nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger)
fmtStr = ">> A single, short paragraph."
assert nwGUI.docEditor.getText()[:29] == fmtStr
- qtbot.wait(stepDelay)
# Centre Align
nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger)
fmtStr = ">> A single, short paragraph. <<"
assert nwGUI.docEditor.getText()[:32] == fmtStr
- qtbot.wait(stepDelay)
# Left Indent
nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger)
fmtStr = "> A single, short paragraph."
assert nwGUI.docEditor.getText()[:28] == fmtStr
- qtbot.wait(stepDelay)
# Right Indent
nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger)
fmtStr = "> A single, short paragraph. <"
assert nwGUI.docEditor.getText()[:30] == fmtStr
- qtbot.wait(stepDelay)
# No Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[:30] == cleanText
- qtbot.wait(stepDelay)
# Other Checks
@@ -368,21 +330,17 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
)
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiMenu_EditFormat
@pytest.mark.gui
-def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
+def testGuiMenu_ContextMenus(qtbot, nwGUI, nwLipsum):
"""Test the context menus.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
assert nwGUI.openProject(nwLipsum)
assert nwGUI.openDocument("4c4f28287af27")
- qtbot.wait(stepDelay)
# Editor Context Menu
theCursor = nwGUI.docEditor.textCursor()
@@ -452,7 +410,7 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.docHeader.backButton.isEnabled()
assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiMenu_ContextMenus
@@ -461,10 +419,6 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the Insert menu.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
buildTestProject(nwGUI, fncProj)
assert nwGUI.projView.projTree._getTreeItem(C.hSceneDoc) is not None
@@ -482,7 +436,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert nwGUI.docEditor.insertText(None) is False
assert nwGUI.docEditor.isEmpty()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# Check Menu Entries
nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger)
@@ -676,12 +630,12 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert nwGUI.docEditor.getText() == "Bar"
# The document isn't empty, so the message box should pop
- monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No)
- assert not nwGUI.importDocument()
- assert nwGUI.docEditor.getText() == "Bar"
+ with monkeypatch.context() as mp:
+ mp.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No)
+ assert not nwGUI.importDocument()
+ assert nwGUI.docEditor.getText() == "Bar"
# Finally, accept the replaced text, this time we use the menu entry to trigger it
- monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.Yes)
nwGUI.mainMenu.aImportFile.activate(QAction.Trigger)
assert nwGUI.docEditor.getText() == "Foo"
@@ -705,6 +659,6 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert theBits[0] == "The currently open file is saved in:"
assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd")
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testGuiMenu_Insert
diff --git a/tests/test_gui/test_gui_noveltree.py b/tests/test_gui/test_gui_noveltree.py
index e38d5555..4ec79000 100644
--- a/tests/test_gui/test_gui_noveltree.py
+++ b/tests/test_gui/test_gui_noveltree.py
@@ -26,7 +26,7 @@ from tools import C, buildTestProject, writeFile
from PyQt5.QtGui import QFocusEvent
from PyQt5.QtCore import Qt, QEvent
-from PyQt5.QtWidgets import QMessageBox, QToolTip
+from PyQt5.QtWidgets import QToolTip
from novelwriter.enum import nwWidget, nwItemType
from novelwriter.gui.noveltree import NovelTreeColumn
@@ -37,9 +37,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test navigating the novel tree.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
buildTestProject(nwGUI, fncProj)
diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py
index 457be313..91467683 100644
--- a/tests/test_gui/test_gui_outline.py
+++ b/tests/test_gui/test_gui_outline.py
@@ -26,7 +26,7 @@ import pytest
from tools import buildTestProject, writeFile
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QWidget, QMessageBox, QAction
+from PyQt5.QtWidgets import QWidget, QAction
from novelwriter.enum import nwItemClass, nwOutline, nwView
@@ -35,10 +35,6 @@ from novelwriter.enum import nwItemClass, nwOutline, nwView
def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
"""Test the outline view.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
# Create a project
prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir)
@@ -156,13 +152,9 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
@pytest.mark.gui
-def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
+def testGuiOutline_Content(qtbot, nwGUI, nwLipsum):
"""Test the outline view.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
assert nwGUI.openProject(nwLipsum)
nwGUI.mainConf.lastPath = nwLipsum
diff --git a/tests/test_gui/test_gui_projtree.py b/tests/test_gui/test_gui_projtree.py
index ecf78341..02efbb9a 100644
--- a/tests/test_gui/test_gui_projtree.py
+++ b/tests/test_gui/test_gui_projtree.py
@@ -40,11 +40,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
projView = nwGUI.projView
@@ -177,11 +172,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
projView = nwGUI.projView
@@ -283,7 +273,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
# Clean up
- # qtbot.stopForInteraction()
+ # qtbot.stop()
nwGUI.closeProject()
# END Test testGuiProjTree_MoveItems
@@ -293,11 +283,6 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test external requests for removing items from project tree.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
projView = nwGUI.projView
@@ -382,11 +367,6 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir,
def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test moving items to Trash.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject
@@ -441,11 +421,6 @@ def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, m
def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test permanently deleting items.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject
@@ -499,11 +474,6 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fnc
def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test emptying Trash.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject
@@ -559,11 +529,6 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test the building of the project tree context menu. All this does
is test that the menu builds. It doesn't open the actual menu,
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
@@ -684,12 +649,6 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, i
"""
mergeData = {}
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.Accepted)
@@ -794,12 +753,6 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, ip
splitData = {}
splitText = []
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QDialog.Accepted)
@@ -907,12 +860,6 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test various parts of the project tree class not covered by
other tests.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
-
# Create a project
prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir)
diff --git a/tests/test_gui/test_gui_statusbar.py b/tests/test_gui/test_gui_statusbar.py
index e2e9a5bb..30d8d861 100644
--- a/tests/test_gui/test_gui_statusbar.py
+++ b/tests/test_gui/test_gui_statusbar.py
@@ -24,18 +24,14 @@ import pytest
from tools import C, buildTestProject
-from PyQt5.QtWidgets import QMessageBox
-
from novelwriter.enum import nwState
from novelwriter.core.document import NWDoc
@pytest.mark.gui
-def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
+def testGuiStatusBar_Main(qtbot, nwGUI, fncProj, mockRnd):
"""Test the the various features of the status bar.
"""
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
buildTestProject(nwGUI, fncProj)
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
newDoc = NWDoc(nwGUI.theProject, cHandle)
diff --git a/tests/test_gui/test_gui_theme.py b/tests/test_gui/test_gui_theme.py
index 0014a4da..cd16ff86 100644
--- a/tests/test_gui/test_gui_theme.py
+++ b/tests/test_gui/test_gui_theme.py
@@ -19,161 +19,428 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
+import os
+import shutil
import pytest
-import novelwriter
-from PyQt5.QtGui import QColor, QPixmap, QIcon
-from PyQt5.QtWidgets import QMessageBox
+from configparser import ConfigParser
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
+from mock import causeOSError
+from novelwriter.constants import nwLabels
+from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
+from tools import writeFile
+
+from PyQt5.QtGui import QIcon, QPalette, QPixmap
+from PyQt5.QtWidgets import QApplication
+
+from novelwriter.config import Config
+from novelwriter.gui.theme import GuiIcons, GuiTheme
@pytest.mark.gui
-def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
- """Test the theme and icon classes.
+def testGuiTheme_Main(qtbot, nwGUI, fncDir):
+ """Test the theme class init.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
+ mainTheme: GuiTheme = nwGUI.mainTheme
+ mainConf: Config = nwGUI.mainConf
- nwGUI = novelwriter.main(
- ["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]
- )
- qtbot.addWidget(nwGUI)
- nwGUI.show()
- qtbot.wait(stepDelay)
+ # Methods
+ # =======
- # Change Settings
- assert novelwriter.CONFIG.confPath == nwMinimal
- novelwriter.CONFIG.guiTheme = "default_dark"
- novelwriter.CONFIG.guiSyntax = "tomorrow_night_eighties"
- novelwriter.CONFIG.guiIcons = "typicons_colour_dark"
- novelwriter.CONFIG.guiFont = "Cantarell"
- novelwriter.CONFIG.guiFontSize = 11
- novelwriter.CONFIG.confChanged = True
- assert novelwriter.CONFIG.saveConfig()
+ mSize = mainTheme.getTextWidth("m")
+ assert mSize > 0
+ assert mainTheme.getTextWidth("m", mainTheme.guiFont) == mSize
- nwGUI.closeMain()
- nwGUI.close()
- del nwGUI
+ # Init Fonts
+ # ==========
- # Re-open
- assert novelwriter.CONFIG.confPath == nwMinimal
- nwGUI = novelwriter.main(
- ["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]
- )
- assert nwGUI.mainConf.confPath == nwMinimal
- qtbot.addWidget(nwGUI)
- nwGUI.show()
- qtbot.wait(stepDelay)
+ # The defaults should be set
+ defaultFont = mainConf.guiFont
+ defaultSize = mainConf.guiFontSize
- assert novelwriter.CONFIG.guiTheme == "default_dark"
- assert novelwriter.CONFIG.guiSyntax == "tomorrow_night_eighties"
- assert novelwriter.CONFIG.guiIcons == "typicons_dark"
- assert novelwriter.CONFIG.guiFont != ""
- assert novelwriter.CONFIG.guiFontSize > 0
+ # CHange them to nonsense values
+ mainConf.guiFont = "notafont"
+ mainConf.guiFontSize = 99
- # Check GUI Colours
- thePalette = nwGUI.palette()
- assert thePalette.window().color() == QColor(54, 54, 54)
- assert thePalette.windowText().color() == QColor(174, 174, 174)
- assert thePalette.base().color() == QColor(62, 62, 62)
- assert thePalette.alternateBase().color() == QColor(78, 78, 78)
- assert thePalette.text().color() == QColor(174, 174, 174)
- assert thePalette.toolTipBase().color() == QColor(255, 255, 192)
- assert thePalette.toolTipText().color() == QColor(21, 21, 13)
- assert thePalette.button().color() == QColor(62, 62, 62)
- assert thePalette.buttonText().color() == QColor(174, 174, 174)
- assert thePalette.brightText().color() == QColor(174, 174, 174)
- assert thePalette.highlight().color() == QColor(44, 152, 247)
- assert thePalette.highlightedText().color() == QColor(255, 255, 255)
- assert thePalette.link().color() == QColor(44, 152, 247)
- assert thePalette.linkVisited().color() == QColor(44, 152, 247)
+ # Let the theme class set them back to default
+ mainTheme._setGuiFont()
+ assert mainConf.guiFont == defaultFont
+ assert mainConf.guiFontSize == defaultSize
- assert nwGUI.mainTheme.statNone == [150, 152, 150]
- assert nwGUI.mainTheme.statSaved == [39, 135, 78]
- assert nwGUI.mainTheme.statUnsaved == [138, 32, 32]
+ # A second call should just restore the defaults again
+ mainTheme._setGuiFont()
+ assert mainConf.guiFont == defaultFont
+ assert mainConf.guiFontSize == defaultSize
- # Check Syntax Colours
- assert nwGUI.mainTheme.colBack == [45, 45, 45]
- assert nwGUI.mainTheme.colText == [204, 204, 204]
- assert nwGUI.mainTheme.colLink == [102, 153, 204]
- assert nwGUI.mainTheme.colHead == [102, 153, 204]
- assert nwGUI.mainTheme.colHeadH == [102, 153, 204]
- assert nwGUI.mainTheme.colEmph == [249, 145, 57]
- assert nwGUI.mainTheme.colDialN == [242, 119, 122]
- assert nwGUI.mainTheme.colDialD == [153, 204, 153]
- assert nwGUI.mainTheme.colDialS == [255, 204, 102]
- assert nwGUI.mainTheme.colHidden == [153, 153, 153]
- assert nwGUI.mainTheme.colKey == [242, 119, 122]
- assert nwGUI.mainTheme.colVal == [204, 153, 204]
- assert nwGUI.mainTheme.colSpell == [242, 119, 122]
- assert nwGUI.mainTheme.colError == [153, 204, 153]
- assert nwGUI.mainTheme.colRepTag == [102, 204, 204]
- assert nwGUI.mainTheme.colMod == [249, 145, 57]
+ # Scan for Themes
+ # ===============
- # Test Icon class
- iconCache = nwGUI.mainTheme.iconCache
- novelwriter.CONFIG.guiIcons = "invalid"
- assert iconCache.updateTheme() is True
- assert novelwriter.CONFIG.guiIcons == "typicons_light"
+ assert mainTheme._listConf({}, "not_a_path") is False
- # Ask for a non-existent key
- anImg = iconCache.loadDecoration("nonsense", 20, 20)
- assert isinstance(anImg, QPixmap)
- assert anImg.isNull()
+ themeOne = os.path.join(fncDir, "themes", "themeone.conf")
+ themeTwo = os.path.join(fncDir, "themes", "themetwo.conf")
+ writeFile(themeOne, "# Stuff")
+ writeFile(themeTwo, "# Stuff")
- # Add a non-existent file and request it
- iconCache.IMAGE_MAP["nonsense"] = "nofile.jpg"
- anImg = iconCache.loadDecoration("nonsense", 20, 20)
- assert isinstance(anImg, QPixmap)
- assert anImg.isNull()
+ result = {}
+ assert mainTheme._listConf(result, os.path.join(fncDir, "themes")) is True
+ assert result["themeone"] == themeOne
+ assert result["themetwo"] == themeTwo
- # Get a real image, with different size parameters
- anImg = iconCache.loadDecoration("wiz-back", 20, None)
- assert isinstance(anImg, QPixmap)
- assert not anImg.isNull()
- assert anImg.width() == 20
- assert anImg.height() >= 56
+ # Parse Colours
+ # =============
- anImg = iconCache.loadDecoration("wiz-back", None, 70)
- assert isinstance(anImg, QPixmap)
- assert not anImg.isNull()
- assert anImg.height() == 70
- assert anImg.width() >= 24
+ parser = ConfigParser()
+ parser["Palette"] = {
+ "colour1": "100, 150, 200",
+ "colour2": "100, 150, 200, 250",
+ "colour3": "250, 250",
+ "colour4": "-10, 127, 300",
+ }
- anImg = iconCache.loadDecoration("wiz-back", 30, 70)
- assert isinstance(anImg, QPixmap)
- assert not anImg.isNull()
- assert anImg.height() == 70
- assert anImg.width() == 30
+ # Test the parser for several valid and invalid values
+ assert mainTheme._parseColour(parser, "Palette", "colour1") == [100, 150, 200]
+ assert mainTheme._parseColour(parser, "Palette", "colour2") == [100, 150, 200]
+ assert mainTheme._parseColour(parser, "Palette", "colour3") == [0, 0, 0]
+ assert mainTheme._parseColour(parser, "Palette", "colour4") == [0, 127, 255]
+ assert mainTheme._parseColour(parser, "Palette", "colour5") == [0, 0, 0]
- anImg = iconCache.loadDecoration("wiz-back", None, None)
- assert isinstance(anImg, QPixmap)
- assert not anImg.isNull()
- assert anImg.height() >= 1500
- assert anImg.width() >= 500
+ # The palette should load with the parsed values
+ mainTheme._setPalette(parser, "Palette", "colour1", QPalette.Window)
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (100, 150, 200, 255)
+ mainTheme._setPalette(parser, "Palette", "colour2", QPalette.Window)
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (100, 150, 200, 255)
+ mainTheme._setPalette(parser, "Palette", "colour3", QPalette.Window)
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 0, 0, 255)
+ mainTheme._setPalette(parser, "Palette", "colour4", QPalette.Window)
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 127, 255, 255)
+ mainTheme._setPalette(parser, "Palette", "colour5", QPalette.Window)
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 0, 0, 255)
- # Load icons
- anIcon = iconCache.getIcon("nonsense")
- assert isinstance(anIcon, QIcon)
- assert anIcon.isNull()
-
- anIcon = iconCache.getIcon("novelwriter")
- assert isinstance(anIcon, QIcon)
- assert not anIcon.isNull()
-
- # Check return empty icon if file not found
- iconCache.ICON_KEYS.add("testicon3")
- anIcon = iconCache.getIcon("testicon3")
- assert isinstance(anIcon, QIcon)
- assert anIcon.isNull()
-
- # qtbot.stopForInteraction()
- nwGUI.closeMain()
- nwGUI.close()
+ # qtbot.stop()
# END Test testGuiTheme_Main
+
+
+@pytest.mark.gui
+def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncDir):
+ """Test the theme part of the class.
+ """
+ mainTheme: GuiTheme = nwGUI.mainTheme
+ mainConf: Config = nwGUI.mainConf
+
+ # List Themes
+ # ===========
+
+ shutil.copy(
+ os.path.join(mainConf.assetPath, "themes", "default_dark.conf"),
+ os.path.join(fncDir, "themes")
+ )
+ shutil.copy(
+ os.path.join(mainConf.assetPath, "themes", "default.conf"),
+ os.path.join(fncDir, "themes")
+ )
+ writeFile(os.path.join(fncDir, "themes", "default.qss"), "/* Stuff */")
+
+ # Block the reading of the files
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert mainTheme.listThemes() == []
+
+ # Load the theme info
+ themesList = mainTheme.listThemes()
+ assert themesList[0] == ("default_dark", "Default Dark Theme")
+ assert themesList[1] == ("default", "Default Theme")
+
+ # A second call should returned the cached list
+ assert mainTheme.listThemes() == mainTheme._themeList
+
+ # Check handling of broken theme settings
+ mainConf.guiTheme = "not_a_theme"
+ assert mainTheme.loadTheme() is False
+
+ # Check handling of unreadable file
+ mainConf.guiTheme = "default"
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert mainTheme.loadTheme() is False
+
+ # Load Default Theme
+ # ==================
+
+ # Set a mock colour for the window background
+ mainTheme._guiPalette.color(QPalette.Window).setRgb(0, 0, 0, 0)
+
+ # Load the default theme
+ mainConf.guiTheme = "default"
+ assert mainTheme.loadTheme() is True
+
+ # This should load a standard palette
+ wCol = QApplication.style().standardPalette().color(QPalette.Window).getRgb()
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == wCol
+
+ # Load Default Dark Theme
+ # =======================
+
+ mainConf.guiTheme = "default_dark"
+ assert mainTheme.loadTheme() is True
+
+ # Check a few values
+ assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (54, 54, 54, 255)
+ assert mainTheme._guiPalette.color(QPalette.WindowText).getRgb() == (174, 174, 174, 255)
+ assert mainTheme._guiPalette.color(QPalette.Base).getRgb() == (62, 62, 62, 255)
+ assert mainTheme._guiPalette.color(QPalette.AlternateBase).getRgb() == (78, 78, 78, 255)
+
+ # qtbot.stop()
+
+# END Test testGuiTheme_Theme
+
+
+@pytest.mark.gui
+def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncDir):
+ """Test the syntax part of the class.
+ """
+ mainTheme: GuiTheme = nwGUI.mainTheme
+ mainConf: Config = nwGUI.mainConf
+
+ # List Themes
+ # ===========
+
+ shutil.copy(
+ os.path.join(mainConf.assetPath, "syntax", "default_dark.conf"),
+ os.path.join(fncDir, "syntax")
+ )
+ shutil.copy(
+ os.path.join(mainConf.assetPath, "syntax", "default_light.conf"),
+ os.path.join(fncDir, "syntax")
+ )
+
+ # Block the reading of the files
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert mainTheme.listThemes() == []
+
+ # Load the syntax info
+ syntaxList = mainTheme.listSyntax()
+ assert syntaxList[0] == ("default_dark", "Default Dark")
+ assert syntaxList[1] == ("default_light", "Default Light")
+
+ # A second call should returned the cached list
+ assert mainTheme.listSyntax() == mainTheme._syntaxList
+
+ # Check handling of broken theme settings
+ mainConf.guiSyntax = "not_a_syntax"
+ assert mainTheme.loadSyntax() is False
+
+ # Check handling of unreadable file
+ mainConf.guiSyntax = "default_light"
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert mainTheme.loadSyntax() is False
+
+ # Load Default Light Syntax
+ # =========================
+
+ # Load the default syntax
+ mainConf.guiSyntax = "default_light"
+ assert mainTheme.loadSyntax() is True
+
+ # Check some values
+ assert mainTheme.syntaxName == "Default Light"
+ assert mainTheme.colBack == [255, 255, 255]
+ assert mainTheme.colText == [0, 0, 0]
+ assert mainTheme.colLink == [0, 0, 200]
+
+ # Load Default Dark Theme
+ # =======================
+
+ # Load the default syntax
+ mainConf.guiSyntax = "default_dark"
+ assert mainTheme.loadSyntax() is True
+
+ # Check some values
+ assert mainTheme.syntaxName == "Default Dark"
+ assert mainTheme.colBack == [54, 54, 54]
+ assert mainTheme.colText == [199, 207, 208]
+ assert mainTheme.colLink == [184, 200, 0]
+
+ # qtbot.stop()
+
+# END Test testGuiTheme_Syntax
+
+
+@pytest.mark.gui
+def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
+ """Test the icon cache class.
+ """
+ iconCache: GuiIcons = nwGUI.mainTheme.iconCache
+ mainConf: Config = nwGUI.mainConf
+
+ # Load Theme
+ # ==========
+
+ # Invalid theme name
+ assert iconCache.loadTheme("not_a_theme") is False
+
+ # Check handling of unreadable file
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert iconCache.loadTheme("typicons_dark") is False
+
+ # Load a broken theme file
+ iconsDir = os.path.join(fncDir, "icons")
+ os.mkdir(iconsDir)
+ os.mkdir(os.path.join(iconsDir, "testicons"))
+ writeFile(os.path.join(iconsDir, "testicons", "icons.conf"), (
+ "[Main]\n"
+ "name = Test Icons\n"
+ "\n"
+ "[Map]\n"
+ "add = add.svg\n"
+ "stuff = stuff.svg\n"
+ ))
+
+ assetPath = mainConf.assetPath
+ mainConf.assetPath = fncDir
+
+ caplog.clear()
+ assert iconCache.loadTheme("testicons") is True
+ assert "Unknown icon name 'stuff' in config file" in caplog.text
+ assert "Icon file 'add.svg' not in theme folder" in caplog.text
+
+ mainConf.assetPath = assetPath
+
+ # Load working theme file
+ assert iconCache.loadTheme("typicons_dark") is True
+ assert "add" in iconCache._themeMap
+
+ # Load Decorations
+ # ================
+
+ # Invalid name should return empty pixmap
+ qPix = iconCache.loadDecoration("stuff")
+ assert qPix.isNull() is True
+
+ # Load an image
+ qPix = iconCache.loadDecoration("wiz-back")
+ assert qPix.isNull() is False
+
+ # Fail finding the file
+ with monkeypatch.context() as mp:
+ mp.setattr("os.path.isfile", lambda *a: False)
+ qPix = iconCache.loadDecoration("wiz-back")
+ assert qPix.isNull() is True
+
+ # Test image sizes
+ qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=None)
+ assert qPix.isNull() is False
+ assert qPix.width() == 100
+ assert qPix.height() > 100
+
+ qPix = iconCache.loadDecoration("wiz-back", pxW=None, pxH=100)
+ assert qPix.isNull() is False
+ assert qPix.width() < 100
+ assert qPix.height() == 100
+
+ qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=100)
+ assert qPix.isNull() is False
+ assert qPix.width() == 100
+ assert qPix.height() == 100
+
+ # Load Icons
+ # ==========
+
+ # Load an unknown icon
+ qIcon = iconCache.getIcon("stuff")
+ assert isinstance(qIcon, QIcon)
+ assert qIcon.isNull() is True
+
+ # Load an icon, it is likelyu already cached
+ qIcon = iconCache.getIcon("add")
+ assert isinstance(qIcon, QIcon)
+ assert qIcon.isNull() is False
+
+ # Load it as a pixmap with a size
+ qPix = iconCache.getPixmap("add", (50, 50))
+ assert isinstance(qPix, QPixmap)
+ assert qPix.isNull() is False
+ assert qPix.width() == 50
+ assert qPix.height() == 50
+
+ # Load app icon
+ qIcon = iconCache.getIcon("novelwriter")
+ assert isinstance(qIcon, QIcon)
+ assert qIcon.isNull() is False
+
+ # Load mime icon
+ qIcon = iconCache.getIcon("proj_nwx")
+ assert isinstance(qIcon, QIcon)
+ assert qIcon.isNull() is False
+
+ # Load Item Icons
+ # ===============
+
+ # Root -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.ROOT, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
+ ) == iconCache.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
+
+ # Folder -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FOLDER, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
+ ) == iconCache.getIcon("proj_folder")
+
+ # Document H0 -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
+ ) == iconCache.getIcon("proj_document")
+
+ # Document H1 -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H1"
+ ) == iconCache.getIcon("proj_title")
+
+ # Document H2 -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H2"
+ ) == iconCache.getIcon("proj_chapter")
+
+ # Document H3 -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H3"
+ ) == iconCache.getIcon("proj_scene")
+
+ # Document H4 -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H4"
+ ) == iconCache.getIcon("proj_section")
+
+ # Document H5 -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H4"
+ ) == iconCache.getIcon("proj_document")
+
+ # Note -> Not Null
+ assert iconCache.getItemIcon(
+ nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NOTE, hLevel="H5"
+ ) == iconCache.getIcon("proj_note")
+
+ # No Type -> Null
+ assert iconCache.getItemIcon(
+ nwItemType.NO_TYPE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H0"
+ ).isNull() is True
+
+ # Header Decorations
+ # ==================
+
+ assert iconCache.getHeaderDecoration(-1) == iconCache._headerDec[0]
+ assert iconCache.getHeaderDecoration(0) == iconCache._headerDec[0]
+ assert iconCache.getHeaderDecoration(1) == iconCache._headerDec[1]
+ assert iconCache.getHeaderDecoration(2) == iconCache._headerDec[2]
+ assert iconCache.getHeaderDecoration(3) == iconCache._headerDec[3]
+ assert iconCache.getHeaderDecoration(4) == iconCache._headerDec[4]
+ assert iconCache.getHeaderDecoration(5) == iconCache._headerDec[4]
+
+ # qtbot.stop()
+
+# END Test testGuiTheme_Icons
diff --git a/tests/test_tools/test_tools_build.py b/tests/test_tools/test_tools_build.py
index 40e30dea..e0088a1c 100644
--- a/tests/test_tools/test_tools_build.py
+++ b/tests/test_tools/test_tools_build.py
@@ -26,22 +26,16 @@ from shutil import copyfile
from tools import cmpFiles, getGuiItem
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QAction, QMessageBox, QFileDialog
+from PyQt5.QtWidgets import QAction, QFileDialog
from novelwriter.tools import GuiBuildNovel
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
"""Test the build tool.
"""
# Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **k: (c, None))
# Check that we cannot open when there is no project
@@ -121,27 +115,17 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Change Title Formats and Flip Switches
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
- qtbot.wait(stepDelay)
nwBuild.fmtScene.setText(r"Scene %ch%.%sc%: %title%")
- qtbot.wait(stepDelay)
nwBuild.fmtSection.setText(r"%ch%.%sc%.1: %title%")
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.justifyText, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeSynopsis, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeKeywords, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.replaceUCode, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
@@ -175,7 +159,6 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Replace Tabs with Spaces
qtbot.mouseClick(nwBuild.replaceTabs, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
@@ -210,20 +193,13 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Putline Mode
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
- qtbot.wait(stepDelay)
nwBuild.fmtScene.setText(r"Scene %sca%: %title%")
- qtbot.wait(stepDelay)
nwBuild.fmtSection.setText(r"Section: %title%")
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeBody, Qt.LeftButton)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
@@ -287,6 +263,6 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
nwBuild._doClose()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testToolBuild_Main
diff --git a/tests/test_tools/test_tools_lipsum.py b/tests/test_tools/test_tools_lipsum.py
index de70a428..0bd07f97 100644
--- a/tests/test_tools/test_tools_lipsum.py
+++ b/tests/test_tools/test_tools_lipsum.py
@@ -23,18 +23,15 @@ import pytest
from tools import C, getGuiItem, buildTestProject
-from PyQt5.QtWidgets import QAction, QMessageBox
+from PyQt5.QtWidgets import QAction
from novelwriter.tools import GuiLipsum
@pytest.mark.gui
-def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
+def testToolLipsum_Main(qtbot, nwGUI, fncProj, mockRnd):
"""Test the Lorem Ipsum tool.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
-
# Check that we cannot open when there is no project
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
assert getGuiItem("GuiLipsum") is None
@@ -70,6 +67,6 @@ def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Close
nwLipsum._doClose()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testToolLipsum_Main
diff --git a/tests/test_tools/test_tools_projwizard.py b/tests/test_tools/test_tools_projwizard.py
index afe720ff..a2facf6e 100644
--- a/tests/test_tools/test_tools_projwizard.py
+++ b/tests/test_tools/test_tools_projwizard.py
@@ -26,7 +26,7 @@ import pytest
from tools import getGuiItem
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QFileDialog, QWizard, QMessageBox, QDialog
+from PyQt5.QtWidgets import QFileDialog, QWizard, QDialog
from novelwriter.enum import nwItemClass
from novelwriter.tools.projwizard import (
@@ -34,10 +34,6 @@ from novelwriter.tools.projwizard import (
ProjWizardPopulatePage, ProjWizardCustomPage, ProjWizardFinalPage
)
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin")
@@ -45,13 +41,8 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the launch of the project wizard.
Disabled for macOS because the test segfaults on QWizard.show()
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
- ##
- # Test New Project Function
- ##
+ # Test New Project Function
+ # ========================
# New with a project open should cause an error
assert nwGUI.openProject(nwMinimal)
@@ -73,9 +64,8 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal})
assert nwGUI.newProject() is False
- ##
- # Test the Wizard Launching
- ##
+ # Test the Wizard Launching
+ # =========================
nwGUI.mainConf.lastPath = " "
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
@@ -87,7 +77,6 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
assert isinstance(nwWiz, GuiProjectWizard)
nwWiz.show()
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.CancelButton), Qt.LeftButton)
assert result is None
@@ -100,7 +89,7 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
nwWiz.reject()
nwWiz.close()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testToolProjectWizard_Handling
@@ -111,14 +100,12 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
"""Test the new project wizard with a set of selection scenarios.
"""
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
nwGUI.mainConf.lastPath = " "
nwWiz = GuiProjectWizard(nwGUI)
nwWiz.show()
- qtbot.wait(stepDelay)
+ qtbot.addWidget(nwWiz)
# Intro Page
# ==========
@@ -134,7 +121,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
# Setting projName should activate the button
assert nwWiz.button(QWizard.NextButton).isEnabled()
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Folder Page
@@ -173,7 +159,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
# Setting projPath should activate the button
assert nwWiz.button(QWizard.NextButton).isEnabled()
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Populate Page
@@ -183,7 +168,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
assert isinstance(popPage, ProjWizardPopulatePage)
assert nwWiz.button(QWizard.NextButton).isEnabled()
- qtbot.wait(stepDelay)
if prjType.startswith("minimal"):
popPage.popMinimal.setChecked(True)
elif prjType.startswith("custom"):
@@ -191,7 +175,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
elif prjType.startswith("sample"):
popPage.popSample.setChecked(True)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Custom Page
@@ -219,7 +202,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
customPage.numChapters.setValue(0)
customPage.numScenes.setValue(10)
- qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Final Page
@@ -264,6 +246,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
nwWiz.reject()
nwWiz.close()
- # qtbot.stopForInteraction()
+ # qtbot.stop()
# END Test testToolProjectWizard_Run
diff --git a/tests/test_tools/test_tools_writingstats.py b/tests/test_tools/test_tools_writingstats.py
index b7358b20..a4ba1963 100644
--- a/tests/test_tools/test_tools_writingstats.py
+++ b/tests/test_tools/test_tools_writingstats.py
@@ -27,26 +27,16 @@ from mock import causeOSError
from tools import getGuiItem, writeFile, buildTestProject
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
+from PyQt5.QtWidgets import QAction, QFileDialog
from novelwriter.tools import GuiWritingStats
from novelwriter.constants import nwFiles
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
-
@pytest.mark.gui
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
"""Test the full writing stats tool.
"""
- # Block message box
- monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
- monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
-
# Create a project to work on
buildTestProject(nwGUI, fncProj)
qtbot.wait(100)
@@ -60,7 +50,6 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
sessLog = getGuiItem("GuiWritingStats")
assert isinstance(sessLog, GuiWritingStats)
- qtbot.wait(stepDelay)
# Test Loading
# ============
@@ -186,9 +175,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# No Novel Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
- qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON)
- qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -234,9 +221,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# No Note Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
- qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON)
- qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -282,11 +267,9 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# No Negative Entries
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
- qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON)
- qtbot.wait(stepDelay)
- # qtbot.stopForInteraction()
+ # qtbot.stop()
jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -316,9 +299,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# Un-hide Zero Entries
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton)
- qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON)
- qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -371,9 +352,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# Group by Day
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
- qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON)
- qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -422,10 +401,9 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
assert not sessLog._loadLogFile()
assert not sessLog._saveData(sessLog.FMT_CSV)
- # qtbot.stopForInteraction()
+ # qtbot.stop()
sessLog._doClose()
assert nwGUI.closeProject()
- qtbot.wait(stepDelay)
# END Test testToolWritingStats_Main