From 80e8788baaea7f558b6bb90803ae290936e4ab84 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 16:20:47 +0200 Subject: [PATCH 01/11] Added scroll past end feature by setting a large root frame margin of the document --- nw/config.py | 2 ++ nw/gui/doceditor.py | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/nw/config.py b/nw/config.py index 5f4a1300..721901ab 100644 --- a/nw/config.py +++ b/nw/config.py @@ -122,6 +122,8 @@ class Config: self.doReplaceDQuote = True self.doReplaceDash = True self.doReplaceDots = True + self.extendScroll = True + self.wordCountTimer = 5.0 self.showTabsNSpaces = False self.showLineEndings = False diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 1d1d2a4e..10f9de3f 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -377,6 +377,7 @@ class GuiDocEditor(QTextEdit): just ensure the margins are set correctly. """ wW = self.width() + wH = self.height() cM = self.mainConf.getTextMargin() vBar = self.verticalScrollBar() @@ -400,7 +401,7 @@ class GuiDocEditor(QTextEdit): tW = wW - 2*tB - sW tH = self.docHeader.height() fH = self.docFooter.height() - fY = self.height() - fH - tB - sH + fY = wH - fH - tB - sH self.docHeader.setGeometry(tB, tB, tW, tH) self.docFooter.setGeometry(tB, fY, tW, fH) @@ -412,7 +413,14 @@ class GuiDocEditor(QTextEdit): else: rH = 0 - self.setViewportMargins(tM, max(cM, tH, rH), tM, max(cM, fH)) + uM = max(cM, tH, rH) + lM = max(cM, fH) + self.setViewportMargins(tM, uM, tM, lM) + + if self.mainConf.extendScroll: + docFrame = self.qDocument.rootFrame().frameFormat() + docFrame.setBottomMargin(wH - uM - lM - self.theTheme.fontPixelSize) + self.qDocument.rootFrame().setFrameFormat(docFrame) return From fe71353198f156ba5a16de6a7cc6a598105fb06c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 16:40:54 +0200 Subject: [PATCH 02/11] Add option to hide scrollbars on all major GUI areas --- nw/config.py | 4 ++++ nw/gui/build.py | 12 ++++++++++-- nw/gui/doceditor.py | 6 ++++++ nw/gui/docviewer.py | 6 ++++++ nw/gui/outline.py | 12 ++++++++++++ nw/gui/outlinedetails.py | 13 +++++++++++++ nw/gui/projtree.py | 14 ++++++++++++++ nw/guimain.py | 3 +++ 8 files changed, 68 insertions(+), 2 deletions(-) diff --git a/nw/config.py b/nw/config.py index 721901ab..24089d93 100644 --- a/nw/config.py +++ b/nw/config.py @@ -102,6 +102,10 @@ class Config: self.outlnPanePos = [500, 150] self.isFullScreen = False + ## Features + self.hideVScroll = False # Hide vertical scroll bars on main widgets + self.hideHScroll = False # Hide horizontal scroll bars on main widgets + ## Project self.autoSaveProj = 60 self.autoSaveDoc = 30 diff --git a/nw/gui/build.py b/nw/gui/build.py index ad9af97b..dc9d13ad 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -450,10 +450,12 @@ class GuiBuildNovel(QDialog): # Tool Box Scroll Area self.toolsArea = QScrollArea() self.toolsArea.setMinimumWidth(self.mainConf.pxInt(250)) - self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.toolsArea.setWidgetResizable(True) self.toolsArea.setWidget(self.toolsWidget) + if self.mainConf.hideVScroll: + self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # Tools and Buttons Layout self.innerBox = QVBoxLayout() @@ -1116,6 +1118,12 @@ class GuiBuildNovelDocView(QTextBrowser): else: self.setTabStopWidth(self.mainConf.getTabWidth()) + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + docPalette = self.palette() docPalette.setColor(QPalette.Base, QColor(255, 255, 255)) docPalette.setColor(QPalette.Text, QColor(0, 0, 0)) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 10f9de3f..85cda2a6 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -224,6 +224,12 @@ class GuiDocEditor(QTextEdit): self.qDocument.setDefaultTextOption(theOpt) + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + # Refresh the tab stops if self.mainConf.verQtValue >= 51000: self.setTabStopDistance(self.mainConf.getTabWidth()) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index b7404e9c..cfd289a4 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -124,6 +124,12 @@ class GuiDocViewer(QTextBrowser): theOpt.setAlignment(Qt.AlignJustify) self.qDocument.setDefaultTextOption(theOpt) + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + # Refresh the tab stops if self.mainConf.verQtValue >= 51000: self.setTabStopDistance(self.mainConf.getTabWidth()) diff --git a/nw/gui/outline.py b/nw/gui/outline.py index 6d524992..3470013f 100644 --- a/nw/gui/outline.py +++ b/nw/gui/outline.py @@ -118,6 +118,7 @@ class GuiOutline(QTreeWidget): self.colIndex = {} self.treeNCols = 0 + self.initOutline() self.clearOutline() self.headerMenu.setHiddenState(self.colHidden) @@ -125,6 +126,17 @@ class GuiOutline(QTreeWidget): return + def initOutline(self): + """Set or update outline settings. + """ + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + + return + def clearOutline(self): """Clear the tree and header and set the default values for the columns arrays. diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py index c6372826..9f116b18 100644 --- a/nw/gui/outlinedetails.py +++ b/nw/gui/outlinedetails.py @@ -224,10 +224,23 @@ class GuiOutlineDetails(QScrollArea): self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setWidgetResizable(True) + self.initDetails() + logger.debug("GuiOutlineDetails initialisation complete") return + def initDetails(self): + """Set or update outline settings. + """ + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + + return + def showItem(self, tHandle, sTitle): """Update the content of the tree with the given handle and line number pointing to a header. diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 858948dc..3c03b236 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -115,6 +115,9 @@ class GuiProjectTree(QTreeWidget): # The last column should just auto-scale self.resizeColumnToContents(self.C_FLAGS) + # Set custom settings + self.initTree() + logger.debug("GuiProjectTree initialisation complete") # Internal Mapping @@ -122,6 +125,17 @@ class GuiProjectTree(QTreeWidget): return + def initTree(self): + """Set or update tree widget settings. + """ + # Scroll bars + if self.mainConf.hideVScroll: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + if self.mainConf.hideHScroll: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + + return + ## # Class Methods ## diff --git a/nw/guimain.py b/nw/guimain.py index 93c62210..18801727 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -790,6 +790,9 @@ class GuiMain(QMainWindow): self.saveDocument() self.docEditor.initEditor() self.docViewer.initViewer() + self.treeView.initTree() + self.projView.initOutline() + self.projMeta.initDetails() return From 1234c9feb69316ab5b44f0f122044ca0e528d176 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 18:21:49 +0200 Subject: [PATCH 03/11] Attemt to scroll upwards like a typewriter --- nw/config.py | 3 ++- nw/gui/doceditor.py | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/nw/config.py b/nw/config.py index 24089d93..88576502 100644 --- a/nw/config.py +++ b/nw/config.py @@ -126,7 +126,8 @@ class Config: self.doReplaceDQuote = True self.doReplaceDash = True self.doReplaceDots = True - self.extendScroll = True + self.scrollPastEnd = True + self.scollWithCursor = False self.wordCountTimer = 5.0 self.showTabsNSpaces = False diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 85cda2a6..f4c87d9b 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -47,7 +47,7 @@ from PyQt5.QtGui import ( from PyQt5.QtWidgets import ( qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox, QWidget, QLabel, QToolBar, QToolButton, QHBoxLayout, QGridLayout, QLineEdit, QPushButton, - QFrame + QFrame, QAbstractSlider ) from nw.core import NWDoc, NWSpellCheck, NWSpellSimple, countWords @@ -88,6 +88,8 @@ class GuiDocEditor(QTextEdit): self.bigDoc = False # Flag for very large document size self.doReplace = False # Switch to temporarily disable auto-replace self.queuePos = None # Used for delayed change of cursor position + self.cursorLast = 0 # The last known vertical position of the cursor + self.lengthLast = 0 # Typography self.typDQOpen = self.mainConf.fmtDoubleQuotes[0] @@ -100,6 +102,8 @@ class GuiDocEditor(QTextEdit): self.qDocument.contentsChange.connect(self._docChange) self.qDocument.documentLayout().documentSizeChanged.connect(self._docSizeChanged) + self.verticalScrollBar().sliderMoved.connect(self._doVerticalScroll) + # Document Title self.docHeader = GuiDocEditHeader(self) self.docFooter = GuiDocEditFooter(self) @@ -319,6 +323,7 @@ class GuiDocEditor(QTextEdit): self.setCursorLine(tLine) self.docFooter.updateLineCount() + self.lengthLast = self.qDocument.characterCount() qApp.restoreOverrideCursor() @@ -423,7 +428,7 @@ class GuiDocEditor(QTextEdit): lM = max(cM, fH) self.setViewportMargins(tM, uM, tM, lM) - if self.mainConf.extendScroll: + if self.mainConf.scrollPastEnd: docFrame = self.qDocument.rootFrame().frameFormat() docFrame.setBottomMargin(wH - uM - lM - self.theTheme.fontPixelSize) self.qDocument.rootFrame().setFrameFormat(docFrame) @@ -757,6 +762,22 @@ class GuiDocEditor(QTextEdit): QTextEdit.keyPressEvent(self, keyEvent) self.docFooter.updateLineCount() + if self.mainConf.scollWithCursor: + docLen = self.qDocument.characterCount() + if docLen == self.lengthLast: + # No change, so just update last position + self.cursorLast = self.cursorRect().center().y() + else: + # The user typed something, so check if we need to + # scroll, and move the scroll bar the same distance + self.lengthLast = docLen + self.ensureCursorVisible() + cPos = self.cursorRect().center().y() + if cPos != self.cursorLast: + vBar = self.verticalScrollBar() + vBar.setValue(vBar.value() + cPos - self.cursorLast) + self.cursorLast = self.cursorRect().center().y() + return def focusNextPrevChild(self, toNext): @@ -782,9 +803,18 @@ class GuiDocEditor(QTextEdit): QTextEdit.mouseReleaseEvent(self, mEvent) self.docFooter.updateLineCount() + self.cursorLast = self.cursorRect().center().y() return + def wheelEvent(self, theEvent): + """Briefly capture the mouse wheel event to capture the cursor + position. + """ + QTextEdit.wheelEvent(self, theEvent) + self.cursorLast = self.cursorRect().center().y() + return + def resizeEvent(self, theEvent): """If the text editor is resize, we must make sure the document has its margins adjusted according to user preferences. @@ -819,6 +849,13 @@ class GuiDocEditor(QTextEdit): self._docAutoReplace(self.qDocument.findBlock(thePos)) return + @pyqtSlot(int) + def _doVerticalScroll(self, theChange): + """Update the cursor position on vertical scrolling. + """ + self.cursorLast = self.cursorRect().center().y() + return + @pyqtSlot("QPoint") def _openContextMenu(self, thePos): """Triggered by right click to open the context menu. Also From 0c0ef6916bfc928ad7434949b0a66b9089f38762 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 19:41:06 +0200 Subject: [PATCH 04/11] Save new config settings and update tests --- nw/config.py | 16 ++++++++++++++++ nw/gui/doceditor.py | 2 +- tests/reference/novelwriter.conf | 6 +++++- tests/reference/novelwriter_prefs.conf | 4 ++++ tests/test_dialogs.py | 2 +- 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/nw/config.py b/nw/config.py index 88576502..5ed12655 100644 --- a/nw/config.py +++ b/nw/config.py @@ -398,6 +398,12 @@ class Config: self.isFullScreen = self._parseLine( cnfParse, cnfSec, "fullscreen", self.CNF_BOOL, self.isFullScreen ) + self.hideVScroll = self._parseLine( + cnfParse, cnfSec, "hidevscroll", self.CNF_BOOL, self.hideVScroll + ) + self.hideHScroll = self._parseLine( + cnfParse, cnfSec, "hidehscroll", self.CNF_BOOL, self.hideHScroll + ) ## Project cnfSec = "Project" @@ -455,6 +461,12 @@ class Config: self.doReplaceDots = self._parseLine( cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots ) + self.scrollPastEnd = self._parseLine( + cnfParse, cnfSec, "scrollpastend", self.CNF_BOOL, self.scrollPastEnd + ) + self.scollWithCursor = self._parseLine( + cnfParse, cnfSec, "scollwithcursor", self.CNF_BOOL, self.scollWithCursor + ) self.fmtSingleQuotes = self._parseLine( cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes ) @@ -572,6 +584,8 @@ class Config: cnfParse.set(cnfSec, "viewpane", self._packList(self.viewPanePos)) cnfParse.set(cnfSec, "outlinepane", self._packList(self.outlnPanePos)) cnfParse.set(cnfSec, "fullscreen", str(self.isFullScreen)) + cnfParse.set(cnfSec, "hidevscroll", str(self.hideVScroll)) + cnfParse.set(cnfSec, "hidehscroll", str(self.hideHScroll)) ## Project cnfSec = "Project" @@ -597,6 +611,8 @@ class Config: cnfParse.set(cnfSec, "repdquotes", str(self.doReplaceDQuote)) cnfParse.set(cnfSec, "repdash", str(self.doReplaceDash)) cnfParse.set(cnfSec, "repdots", str(self.doReplaceDots)) + cnfParse.set(cnfSec, "scrollpastend", str(self.scrollPastEnd)) + cnfParse.set(cnfSec, "scollwithcursor", str(self.scollWithCursor)) cnfParse.set(cnfSec, "fmtsinglequote", self._packList(self.fmtSingleQuotes)) cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes)) cnfParse.set(cnfSec, "spelltool", str(self.spellTool)) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index f4c87d9b..fe9581e9 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -430,7 +430,7 @@ class GuiDocEditor(QTextEdit): if self.mainConf.scrollPastEnd: docFrame = self.qDocument.rootFrame().frameFormat() - docFrame.setBottomMargin(wH - uM - lM - self.theTheme.fontPixelSize) + docFrame.setBottomMargin(wH - uM - lM - 4*tB - self.theTheme.fontPixelSize) self.qDocument.rootFrame().setFrameFormat(docFrame) return diff --git a/tests/reference/novelwriter.conf b/tests/reference/novelwriter.conf index 49054ae7..f4bc8d61 100644 --- a/tests/reference/novelwriter.conf +++ b/tests/reference/novelwriter.conf @@ -1,5 +1,5 @@ [Main] -timestamp = 2020-06-29 17:34:15 +timestamp = 2020-10-11 18:29:34 theme = default syntax = default_light icons = typicons_colour_light @@ -16,6 +16,8 @@ docpane = 400, 400 viewpane = 500, 150 outlinepane = 500, 150 fullscreen = False +hidevscroll = False +hidehscroll = False [Project] autosaveproject = 60 @@ -37,6 +39,8 @@ repsquotes = True repdquotes = True repdash = True repdots = True +scrollpastend = True +scollwithcursor = False fmtsinglequote = ‘, ’ fmtdoublequote = “, ” spelltool = internal diff --git a/tests/reference/novelwriter_prefs.conf b/tests/reference/novelwriter_prefs.conf index 608116ea..77e216bd 100644 --- a/tests/reference/novelwriter_prefs.conf +++ b/tests/reference/novelwriter_prefs.conf @@ -16,6 +16,8 @@ docpane = 400, 400 viewpane = 500, 150 outlinepane = 500, 150 fullscreen = False +hidevscroll = False +hidehscroll = False [Project] autosaveproject = 40 @@ -37,6 +39,8 @@ repsquotes = True repdquotes = True repdash = True repdots = True +scrollpastend = True +scollwithcursor = False fmtsinglequote = ‘, ’ fmtdoublequote = “, ” spelltool = internal diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index 9d05f56f..11d0cd05 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -1198,7 +1198,7 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC ignoreLines = [ 2, # Timestamp 11, 12, 13, 14, 15, 16, 17, # Window sizes - 7, 25, # Fonts (depends on system default) + 7, 27, # Fonts (depends on system default) ] assert cmpFiles(testConf, refConf, ignoreLines) From fa55983f3abdeae1d4e83fc900b8bc73a9fad7ea Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 19:57:00 +0200 Subject: [PATCH 05/11] Split General tab in Preferences into General and Projects --- nw/gui/preferences.py | 118 ++++++++++++++++++++++++++++-------------- tests/test_dialogs.py | 36 ++++++++----- 2 files changed, 101 insertions(+), 53 deletions(-) diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index a946c289..5cba9fda 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -55,15 +55,17 @@ class GuiPreferences(PagedDialog): self.setWindowTitle("Preferences") - self.tabGeneral = GuiConfigEditGeneralTab(self.theParent) - self.tabLayout = GuiConfigEditLayoutTab(self.theParent) - self.tabEditing = GuiConfigEditEditingTab(self.theParent) - self.tabAutoRep = GuiConfigEditAutoReplaceTab(self.theParent) + self.tabGeneral = GuiConfigEditGeneralTab(self.theParent) + self.tabProjects = GuiConfigEditProjectsTab(self.theParent) + self.tabLayout = GuiConfigEditLayoutTab(self.theParent) + self.tabEditing = GuiConfigEditEditingTab(self.theParent) + self.tabAutoRep = GuiConfigEditAutoReplaceTab(self.theParent) - self.addTab(self.tabGeneral, "General") - self.addTab(self.tabLayout, "Text Layout") - self.addTab(self.tabEditing, "Editor") - self.addTab(self.tabAutoRep, "Auto-Replace") + self.addTab(self.tabGeneral, "General") + self.addTab(self.tabProjects, "Projects") + self.addTab(self.tabLayout, "Text Layout") + self.addTab(self.tabEditing, "Editor") + self.addTab(self.tabAutoRep, "Auto-Replace") self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self._doSave) @@ -91,6 +93,10 @@ class GuiPreferences(PagedDialog): validEntries &= retA needsRestart |= retB + retA, retB = self.tabProjects.saveValues() + validEntries &= retA + needsRestart |= retB + retA, retB = self.tabLayout.saveValues() validEntries &= retA needsRestart |= retB @@ -222,6 +228,70 @@ class GuiConfigEditGeneralTab(QWidget): self.showFullPath ) + return + + def saveValues(self): + """Save the values set for this tab. + """ + validEntries = True + needsRestart = False + + guiTheme = self.selectTheme.currentData() + guiIcons = self.selectIcons.currentData() + guiDark = self.preferDarkIcons.isChecked() + guiFont = self.guiFont.text() + guiFontSize = self.guiFontSize.value() + showFullPath = self.showFullPath.isChecked() + + # Check if restart is needed + needsRestart |= self.mainConf.guiTheme != guiTheme + needsRestart |= self.mainConf.guiIcons != guiIcons + needsRestart |= self.mainConf.guiFont != guiFont + needsRestart |= self.mainConf.guiFontSize != guiFontSize + + self.mainConf.guiTheme = guiTheme + self.mainConf.guiIcons = guiIcons + self.mainConf.guiDark = guiDark + self.mainConf.guiFont = guiFont + self.mainConf.guiFontSize = guiFontSize + self.mainConf.showFullPath = showFullPath + + self.mainConf.confChanged = True + + return validEntries, needsRestart + + ## + # Slots + ## + + def _selectFont(self): + """Open the QFontDialog and set a font for the font style. + """ + currFont = QFont() + currFont.setFamily(self.mainConf.guiFont) + currFont.setPointSize(self.mainConf.guiFontSize) + theFont, theStatus = QFontDialog.getFont(currFont, self) + if theStatus: + self.guiFont.setText(theFont.family()) + self.guiFontSize.setValue(theFont.pointSize()) + return + +# END Class GuiConfigEditGeneralTab + +class GuiConfigEditProjectsTab(QWidget): + + def __init__(self, theParent): + QWidget.__init__(self, theParent) + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theTheme = theParent.theTheme + + # The Form + self.mainForm = QConfigLayout() + self.mainForm.setHelpTextStyle(self.theTheme.helpText) + self.setLayout(self.mainForm) + # AutoSave Settings # ================= self.mainForm.addGroupLabel("Automatic Save") @@ -292,30 +362,12 @@ class GuiConfigEditGeneralTab(QWidget): validEntries = True needsRestart = False - guiTheme = self.selectTheme.currentData() - guiIcons = self.selectIcons.currentData() - guiDark = self.preferDarkIcons.isChecked() - guiFont = self.guiFont.text() - guiFontSize = self.guiFontSize.value() - showFullPath = self.showFullPath.isChecked() autoSaveDoc = self.autoSaveDoc.value() autoSaveProj = self.autoSaveProj.value() backupPath = self.backupPath backupOnClose = self.backupOnClose.isChecked() askBeforeBackup = self.askBeforeBackup.isChecked() - # Check if restart is needed - needsRestart |= self.mainConf.guiTheme != guiTheme - needsRestart |= self.mainConf.guiIcons != guiIcons - needsRestart |= self.mainConf.guiFont != guiFont - needsRestart |= self.mainConf.guiFontSize != guiFontSize - - self.mainConf.guiTheme = guiTheme - self.mainConf.guiIcons = guiIcons - self.mainConf.guiDark = guiDark - self.mainConf.guiFont = guiFont - self.mainConf.guiFontSize = guiFontSize - self.mainConf.showFullPath = showFullPath self.mainConf.autoSaveDoc = autoSaveDoc self.mainConf.autoSaveProj = autoSaveProj self.mainConf.backupPath = backupPath @@ -357,19 +409,7 @@ class GuiConfigEditGeneralTab(QWidget): self.askBeforeBackup.setEnabled(theState) return - def _selectFont(self): - """Open the QFontDialog and set a font for the font style. - """ - currFont = QFont() - currFont.setFamily(self.mainConf.guiFont) - currFont.setPointSize(self.mainConf.guiFontSize) - theFont, theStatus = QFontDialog.getFont(currFont, self) - if theStatus: - self.guiFont.setText(theFont.family()) - self.guiFontSize.setValue(theFont.pointSize()) - return - -# END Class GuiConfigEditGeneralTab +# END Class GuiConfigEditProjectsTab class GuiConfigEditLayoutTab(QWidget): diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index 11d0cd05..cf04284b 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -1057,6 +1057,7 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC nwGUI.mainConf = tmpConf nwPrefs.mainConf = tmpConf nwPrefs.tabGeneral.mainConf = tmpConf + nwPrefs.tabProjects.mainConf = tmpConf nwPrefs.tabLayout.mainConf = tmpConf nwPrefs.tabEditing.mainConf = tmpConf nwPrefs.tabAutoRep.mainConf = tmpConf @@ -1065,7 +1066,6 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC qtbot.wait(keyDelay) tabGeneral = nwPrefs.tabGeneral nwPrefs._tabBox.setCurrentWidget(tabGeneral) - tabGeneral.backupPath = "no/where" qtbot.wait(keyDelay) assert not tabGeneral.preferDarkIcons.isChecked() @@ -1077,27 +1077,35 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton) assert not tabGeneral.showFullPath.isChecked() - # Check Browse button - monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "") - assert not tabGeneral._backupFolder() - monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "some/dir") - qtbot.mouseClick(tabGeneral.backupGetPath, Qt.LeftButton) - # Check font button monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True)) qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton) qtbot.wait(keyDelay) - assert not tabGeneral.backupOnClose.isChecked() - qtbot.mouseClick(tabGeneral.backupOnClose, Qt.LeftButton) - assert tabGeneral.backupOnClose.isChecked() + tabGeneral.guiFontSize.setValue(12) + + # Projects Settings + qtbot.wait(keyDelay) + tabProjects = nwPrefs.tabProjects + nwPrefs._tabBox.setCurrentWidget(tabProjects) + tabProjects.backupPath = "no/where" qtbot.wait(keyDelay) - tabGeneral.guiFontSize.setValue(12) - tabGeneral.autoSaveDoc.setValue(20) - tabGeneral.autoSaveProj.setValue(40) + assert not tabProjects.backupOnClose.isChecked() + qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton) + assert tabProjects.backupOnClose.isChecked() - # Text Layour Settings + # Check Browse button + monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "") + assert not tabProjects._backupFolder() + monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "some/dir") + qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton) + + qtbot.wait(keyDelay) + tabProjects.autoSaveDoc.setValue(20) + tabProjects.autoSaveProj.setValue(40) + + # Text Layout Settings qtbot.wait(keyDelay) tabLayout = nwPrefs.tabLayout nwPrefs._tabBox.setCurrentWidget(tabLayout) From ae8ec555fd1de3d3a65f4688193e6216c1df3a84 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 20:13:39 +0200 Subject: [PATCH 06/11] Connected the new settings to the Preferences dialog --- nw/gui/build.py | 11 ++++++++++ nw/gui/doceditor.py | 10 ++++++++- nw/gui/docviewer.py | 5 +++++ nw/gui/outline.py | 5 +++++ nw/gui/outlinedetails.py | 5 +++++ nw/gui/preferences.py | 45 +++++++++++++++++++++++++++++++++++++++- nw/gui/projtree.py | 5 +++++ 7 files changed, 84 insertions(+), 2 deletions(-) diff --git a/nw/gui/build.py b/nw/gui/build.py index dc9d13ad..754553d8 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -452,10 +452,16 @@ class GuiBuildNovel(QDialog): self.toolsArea.setMinimumWidth(self.mainConf.pxInt(250)) self.toolsArea.setWidgetResizable(True) self.toolsArea.setWidget(self.toolsWidget) + if self.mainConf.hideVScroll: self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) # Tools and Buttons Layout self.innerBox = QVBoxLayout() @@ -1121,8 +1127,13 @@ class GuiBuildNovelDocView(QTextBrowser): # Scroll bars if self.mainConf.hideVScroll: self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) docPalette = self.palette() docPalette.setColor(QPalette.Base, QColor(255, 255, 255)) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index fe9581e9..e459b1b1 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -47,7 +47,7 @@ from PyQt5.QtGui import ( from PyQt5.QtWidgets import ( qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox, QWidget, QLabel, QToolBar, QToolButton, QHBoxLayout, QGridLayout, QLineEdit, QPushButton, - QFrame, QAbstractSlider + QFrame ) from nw.core import NWDoc, NWSpellCheck, NWSpellSimple, countWords @@ -231,8 +231,13 @@ class GuiDocEditor(QTextEdit): # Scroll bars if self.mainConf.hideVScroll: self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) # Refresh the tab stops if self.mainConf.verQtValue >= 51000: @@ -479,11 +484,14 @@ class GuiDocEditor(QTextEdit): """ if not isinstance(thePosition, int): return False + if thePosition >= 0: theCursor = self.textCursor() theCursor.setPosition(thePosition) self.setTextCursor(theCursor) self.docFooter.updateLineCount() + self.cursorLast = self.cursorRect().center().y() + return True def getCursorPosition(self): diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index cfd289a4..2d9ac637 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -127,8 +127,13 @@ class GuiDocViewer(QTextBrowser): # Scroll bars if self.mainConf.hideVScroll: self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) # Refresh the tab stops if self.mainConf.verQtValue >= 51000: diff --git a/nw/gui/outline.py b/nw/gui/outline.py index 3470013f..d3dc4815 100644 --- a/nw/gui/outline.py +++ b/nw/gui/outline.py @@ -132,8 +132,13 @@ class GuiOutline(QTreeWidget): # Scroll bars if self.mainConf.hideVScroll: self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) return diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py index 9f116b18..49998dd9 100644 --- a/nw/gui/outlinedetails.py +++ b/nw/gui/outlinedetails.py @@ -236,8 +236,13 @@ class GuiOutlineDetails(QScrollArea): # Scroll bars if self.mainConf.hideVScroll: self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) return diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index 5cba9fda..f1848bb3 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -225,7 +225,24 @@ class GuiConfigEditGeneralTab(QWidget): self.showFullPath.setChecked(self.mainConf.showFullPath) self.mainForm.addRow( "Show full path in document header", - self.showFullPath + self.showFullPath, + "Shows the document title and parent folder names." + ) + + self.hideVScroll = QSwitch() + self.hideVScroll.setChecked(self.mainConf.hideVScroll) + self.mainForm.addRow( + "Hide vertical scroll bars in main windows", + self.hideVScroll, + "Scrolling with mouse wheel and keys only." + ) + + self.hideHScroll = QSwitch() + self.hideHScroll.setChecked(self.mainConf.hideHScroll) + self.mainForm.addRow( + "Hide horizontal scroll bars in main windows", + self.hideHScroll, + "Scrolling with mouse wheel and keys only." ) return @@ -242,6 +259,8 @@ class GuiConfigEditGeneralTab(QWidget): guiFont = self.guiFont.text() guiFontSize = self.guiFontSize.value() showFullPath = self.showFullPath.isChecked() + hideVScroll = self.hideVScroll.isChecked() + hideHScroll = self.hideHScroll.isChecked() # Check if restart is needed needsRestart |= self.mainConf.guiTheme != guiTheme @@ -255,6 +274,8 @@ class GuiConfigEditGeneralTab(QWidget): self.mainConf.guiFont = guiFont self.mainConf.guiFontSize = guiFontSize self.mainConf.showFullPath = showFullPath + self.mainConf.hideVScroll = hideVScroll + self.mainConf.hideHScroll = hideHScroll self.mainConf.confChanged = True @@ -534,6 +555,24 @@ class GuiConfigEditLayoutTab(QWidget): theUnit="px" ) + ## Scroll Past End + self.scrollPastEnd = QSwitch() + self.scrollPastEnd.setChecked(self.mainConf.scrollPastEnd) + self.mainForm.addRow( + "Scroll past end of the document", + self.scrollPastEnd, + "Allows scrolling until last line is at the top." + ) + + ## Typewriter Scrolling + self.scollWithCursor = QSwitch() + self.scollWithCursor.setChecked(self.mainConf.scollWithCursor) + self.mainForm.addRow( + "Typewriter style scrolling", + self.scollWithCursor, + "Scrolls up when the cursor moves to a new line." + ) + return def saveValues(self): @@ -551,6 +590,8 @@ class GuiConfigEditLayoutTab(QWidget): doJustify = self.textJustify.isChecked() textMargin = self.textMargin.value() tabWidth = self.tabWidth.value() + scrollPastEnd = self.scrollPastEnd.isChecked() + scollWithCursor = self.scollWithCursor.isChecked() self.mainConf.textFont = textFont self.mainConf.textSize = textSize @@ -561,6 +602,8 @@ class GuiConfigEditLayoutTab(QWidget): self.mainConf.doJustify = doJustify self.mainConf.textMargin = textMargin self.mainConf.tabWidth = tabWidth + self.mainConf.scrollPastEnd = scrollPastEnd + self.mainConf.scollWithCursor = scollWithCursor self.mainConf.confChanged = True diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 3c03b236..03dc2ff8 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -131,8 +131,13 @@ class GuiProjectTree(QTreeWidget): # Scroll bars if self.mainConf.hideVScroll: self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + if self.mainConf.hideHScroll: self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + else: + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) return From 2382118dad25870297fabb02f4e3b9973f6f1bd4 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 20:18:58 +0200 Subject: [PATCH 07/11] Add test coverage of the new options --- tests/reference/novelwriter_prefs.conf | 8 ++++---- tests/test_dialogs.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/reference/novelwriter_prefs.conf b/tests/reference/novelwriter_prefs.conf index 77e216bd..0e1d41f6 100644 --- a/tests/reference/novelwriter_prefs.conf +++ b/tests/reference/novelwriter_prefs.conf @@ -16,8 +16,8 @@ docpane = 400, 400 viewpane = 500, 150 outlinepane = 500, 150 fullscreen = False -hidevscroll = False -hidehscroll = False +hidevscroll = True +hidehscroll = True [Project] autosaveproject = 40 @@ -39,8 +39,8 @@ repsquotes = True repdquotes = True repdash = True repdots = True -scrollpastend = True -scollwithcursor = False +scrollpastend = False +scollwithcursor = True fmtsinglequote = ‘, ’ fmtdoublequote = “, ” spelltool = internal diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index cf04284b..1863d991 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -1077,6 +1077,16 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton) assert not tabGeneral.showFullPath.isChecked() + qtbot.wait(keyDelay) + assert not tabGeneral.hideVScroll.isChecked() + qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton) + assert tabGeneral.hideVScroll.isChecked() + + qtbot.wait(keyDelay) + assert not tabGeneral.hideHScroll.isChecked() + qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton) + assert tabGeneral.hideHScroll.isChecked() + # Check font button monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True)) qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton) @@ -1135,6 +1145,16 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC qtbot.mouseClick(tabLayout.textJustify, Qt.LeftButton) assert not tabLayout.textJustify.isChecked() + qtbot.wait(keyDelay) + assert tabLayout.scrollPastEnd.isChecked() + qtbot.mouseClick(tabLayout.scrollPastEnd, Qt.LeftButton) + assert not tabLayout.scrollPastEnd.isChecked() + + qtbot.wait(keyDelay) + assert not tabLayout.scollWithCursor.isChecked() + qtbot.mouseClick(tabLayout.scollWithCursor, Qt.LeftButton) + assert tabLayout.scollWithCursor.isChecked() + # Editor Settings qtbot.wait(keyDelay) tabEditing = nwPrefs.tabEditing From 710ad598d99cec0974f47dcfa5ebbfeb3c67dcbc Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 20:34:51 +0200 Subject: [PATCH 08/11] Count words when saving editor text --- nw/gui/doceditor.py | 5 +++++ nw/gui/projtree.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 1d1d2a4e..13c67650 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -360,9 +360,14 @@ class GuiDocEditor(QTextEdit): return False docText = self.getText() + + cC, wC, pC = countWords(docText) + self._updateCounts(cC, wC, pC) + theItem.setCharCount(self.charCount) theItem.setWordCount(self.wordCount) theItem.setParaCount(self.paraCount) + self.saveCursorPosition() self.nwDocument.saveDocument(docText) self.setDocumentChanged(False) diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 858948dc..2d8900cf 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -241,7 +241,8 @@ class GuiProjectTree(QTreeWidget): # Add the new item to the tree if tHandle is not None: self.revealNewTreeItem(tHandle, nHandle) - self.theParent.editItem(tHandle) + if self.mainConf.showGUI: + self.theParent.editItem(tHandle) return True From 92c87872adbc7da14d7cb841e6e220866f4decdb Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 20:58:45 +0200 Subject: [PATCH 09/11] Try to fix writing stats test that keeps randomly failing --- tests/test_dialogs.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index 1863d991..1b9cf880 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -228,6 +228,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): # Create new, save, close project nwGUI.theProject.projTree.setSeed(42) assert nwGUI.newProject({"projPath": nwFuncTemp}) + qtbot.wait(200) assert nwGUI.saveProject() assert nwGUI.closeProject() qtbot.wait(stepDelay) @@ -236,11 +237,12 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger) assert getGuiItem("GuiWritingStats") is None + # Add some text to the scene file assert nwGUI.openProject(nwFuncTemp) qtbot.wait(stepDelay) - # Add some text to the scene file assert nwGUI.openDocument("0e17daca5f3e1") + nwGUI.docEditor.clear() assert nwGUI.docEditor.insertText( "# Scene One\n\n" "It was the best of times, it was the worst of times, it was the age of wisdom, it was " @@ -252,10 +254,18 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): "insisted on its being received, for good or for evil, in the superlative degree of " "comparison only.\n\n" ) + qtbot.wait(stepDelay) assert nwGUI.saveDocument() + qtbot.wait(200) # Ensures that the session length is > 0 + + assert nwGUI.saveProject() + assert nwGUI.closeProject() + qtbot.wait(stepDelay) # Add a note file with some text - nwGUI.setFocus(1) + assert nwGUI.openProject(nwFuncTemp) + qtbot.wait(stepDelay) + nwGUI.treeView.clearSelection() nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True) nwGUI.treeView.newTreeItem(nwItemType.FILE, None) @@ -264,8 +274,9 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): "# Jane Doe\n\n" "All about Jane.\n\n" ) + qtbot.wait(stepDelay) assert nwGUI.saveDocument() - qtbot.wait(500) # Ensures that the session length is > 0 + qtbot.wait(200) # Ensures that the session length is > 0 assert nwGUI.saveProject() assert nwGUI.closeProject() @@ -296,11 +307,12 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.loads(inFile.read()) - assert len(jsonData) == 2 + qtbot.wait(stepDelay) + assert len(jsonData) == 3 assert jsonData[1]["length"] >= 0 - assert jsonData[1]["newWords"] == 126 - assert jsonData[1]["novelWords"] == 127 - assert jsonData[1]["noteWords"] == 5 + assert jsonData[1]["newWords"] == 119 + assert jsonData[1]["novelWords"] == 125 + assert jsonData[1]["noteWords"] == 0 # No Novel Files qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) @@ -315,7 +327,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): assert len(jsonData) == 1 assert jsonData[0]["length"] >= 0 assert jsonData[0]["newWords"] == 5 - assert jsonData[0]["novelWords"] == 127 + assert jsonData[0]["novelWords"] == 125 assert jsonData[0]["noteWords"] == 5 # No Note Files @@ -331,9 +343,9 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): assert len(jsonData) == 2 assert jsonData[1]["length"] >= 0 - assert jsonData[1]["newWords"] == 121 - assert jsonData[1]["novelWords"] == 127 - assert jsonData[1]["noteWords"] == 5 + assert jsonData[1]["newWords"] == 119 + assert jsonData[1]["novelWords"] == 125 + assert jsonData[1]["noteWords"] == 0 # No Negative Entries qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) @@ -346,7 +358,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.loads(inFile.read()) - assert len(jsonData) == 2 + assert len(jsonData) == 3 # Un-hide Zero Entries qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) @@ -359,7 +371,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.loads(inFile.read()) - assert len(jsonData) == 2 + assert len(jsonData) == 3 # Group by Day qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) From 6ee326e7168eae7fbc48909703320b7581006e40 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 21:21:33 +0200 Subject: [PATCH 10/11] Hardcode the values of the session stats log --- tests/test_dialogs.py | 76 ++++++++++++------------------------------- 1 file changed, 20 insertions(+), 56 deletions(-) diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index 1b9cf880..a78a684b 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -23,7 +23,7 @@ from nw.gui import ( GuiProjectLoad, GuiPreferences ) from nw.gui.custom import QuotesDialog -from nw.constants import nwItemType, nwItemLayout, nwItemClass +from nw.constants import nwItemType, nwItemLayout, nwItemClass, nwFiles keyDelay = 2 typeDelay = 1 @@ -233,54 +233,15 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): assert nwGUI.closeProject() qtbot.wait(stepDelay) - # Check that we cannot open when there is no project - nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger) - assert getGuiItem("GuiWritingStats") is None - - # Add some text to the scene file - assert nwGUI.openProject(nwFuncTemp) - qtbot.wait(stepDelay) - - assert nwGUI.openDocument("0e17daca5f3e1") - nwGUI.docEditor.clear() - assert nwGUI.docEditor.insertText( - "# Scene One\n\n" - "It was the best of times, it was the worst of times, it was the age of wisdom, it was " - "the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it " - "was the season of Light, it was the season of Darkness, it was the spring of hope, it " - "was the winter of despair, we had everything before us, we had nothing before us, we " - "were all going direct to Heaven, we were all going direct the other way – in short, the " - "period was so far like the present period, that some of its noisiest authorities " - "insisted on its being received, for good or for evil, in the superlative degree of " - "comparison only.\n\n" - ) - qtbot.wait(stepDelay) - assert nwGUI.saveDocument() - qtbot.wait(200) # Ensures that the session length is > 0 - - assert nwGUI.saveProject() - assert nwGUI.closeProject() - qtbot.wait(stepDelay) - - # Add a note file with some text - assert nwGUI.openProject(nwFuncTemp) - qtbot.wait(stepDelay) - - nwGUI.treeView.clearSelection() - nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True) - nwGUI.treeView.newTreeItem(nwItemType.FILE, None) - assert nwGUI.openSelectedItem() - assert nwGUI.docEditor.insertText( - "# Jane Doe\n\n" - "All about Jane.\n\n" - ) - qtbot.wait(stepDelay) - assert nwGUI.saveDocument() - qtbot.wait(200) # Ensures that the session length is > 0 - - assert nwGUI.saveProject() - assert nwGUI.closeProject() - qtbot.wait(stepDelay) + sessFile = os.path.join(nwFuncTemp, "meta", nwFiles.SESS_STATS) + with open(sessFile, mode="w+", encoding="utf-8") as outFile: + outFile.write( + "# Start Time End Time Novel Notes\n" + "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n" + "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" + "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" + "2020-01-06 21:00:00 2020-01-06 21:00:10 125 5\n" + ) # Open again, and check the stats assert nwGUI.openProject(nwFuncTemp) @@ -299,17 +260,18 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, "")) assert sessLog._saveData(sessLog.FMT_CSV) - qtbot.wait(stepDelay) + qtbot.wait(100) assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(stepDelay) + qtbot.wait(100) jsonStats = os.path.join(nwFuncTemp, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.loads(inFile.read()) qtbot.wait(stepDelay) + assert len(jsonData) == 3 - assert jsonData[1]["length"] >= 0 + assert jsonData[1]["length"] >= 14.0 assert jsonData[1]["newWords"] == 119 assert jsonData[1]["novelWords"] == 125 assert jsonData[1]["noteWords"] == 0 @@ -325,7 +287,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): jsonData = json.loads(inFile.read()) assert len(jsonData) == 1 - assert jsonData[0]["length"] >= 0 + assert jsonData[0]["length"] >= 14.0 assert jsonData[0]["newWords"] == 5 assert jsonData[0]["novelWords"] == 125 assert jsonData[0]["noteWords"] == 5 @@ -342,7 +304,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): jsonData = json.loads(inFile.read()) assert len(jsonData) == 2 - assert jsonData[1]["length"] >= 0 + assert jsonData[1]["length"] >= 14.0 assert jsonData[1]["newWords"] == 119 assert jsonData[1]["novelWords"] == 125 assert jsonData[1]["noteWords"] == 0 @@ -371,7 +333,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.loads(inFile.read()) - assert len(jsonData) == 3 + assert len(jsonData) == 4 # Group by Day qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) @@ -385,11 +347,13 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp): # Check against both 1 and 2 as this can be 2 if test was started just before midnight. # A failed test should in any case produce a 4 - assert len(jsonData) in (1, 2) + assert len(jsonData) == 3 # qtbot.stopForInteraction() sessLog._doClose() + assert nwGUI.closeProject() + qtbot.wait(stepDelay) nwGUI.closeMain() @pytest.mark.gui From 870c5d83435f7cd56254e9e4d903d533eb526b66 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 11 Oct 2020 21:23:39 +0200 Subject: [PATCH 11/11] Fix flake8 test --- tests/test_dialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index a78a684b..ddc77738 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -23,7 +23,7 @@ from nw.gui import ( GuiProjectLoad, GuiPreferences ) from nw.gui.custom import QuotesDialog -from nw.constants import nwItemType, nwItemLayout, nwItemClass, nwFiles +from nw.constants import nwItemLayout, nwItemClass, nwFiles keyDelay = 2 typeDelay = 1