Merge pull request #468 from vkbo/scrolling

Scrolling Options
This commit is contained in:
Veronica K. Berglyd Olsen
2020-10-11 21:27:53 +02:00
committed by GitHub
12 changed files with 385 additions and 115 deletions
+23
View File
@@ -102,6 +102,10 @@ class Config:
self.outlnPanePos = [500, 150] self.outlnPanePos = [500, 150]
self.isFullScreen = False 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 ## Project
self.autoSaveProj = 60 self.autoSaveProj = 60
self.autoSaveDoc = 30 self.autoSaveDoc = 30
@@ -122,6 +126,9 @@ class Config:
self.doReplaceDQuote = True self.doReplaceDQuote = True
self.doReplaceDash = True self.doReplaceDash = True
self.doReplaceDots = True self.doReplaceDots = True
self.scrollPastEnd = True
self.scollWithCursor = False
self.wordCountTimer = 5.0 self.wordCountTimer = 5.0
self.showTabsNSpaces = False self.showTabsNSpaces = False
self.showLineEndings = False self.showLineEndings = False
@@ -391,6 +398,12 @@ class Config:
self.isFullScreen = self._parseLine( self.isFullScreen = self._parseLine(
cnfParse, cnfSec, "fullscreen", self.CNF_BOOL, self.isFullScreen 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 ## Project
cnfSec = "Project" cnfSec = "Project"
@@ -448,6 +461,12 @@ class Config:
self.doReplaceDots = self._parseLine( self.doReplaceDots = self._parseLine(
cnfParse, cnfSec, "repdots", self.CNF_BOOL, self.doReplaceDots 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( self.fmtSingleQuotes = self._parseLine(
cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes
) )
@@ -565,6 +584,8 @@ class Config:
cnfParse.set(cnfSec, "viewpane", self._packList(self.viewPanePos)) cnfParse.set(cnfSec, "viewpane", self._packList(self.viewPanePos))
cnfParse.set(cnfSec, "outlinepane", self._packList(self.outlnPanePos)) cnfParse.set(cnfSec, "outlinepane", self._packList(self.outlnPanePos))
cnfParse.set(cnfSec, "fullscreen", str(self.isFullScreen)) cnfParse.set(cnfSec, "fullscreen", str(self.isFullScreen))
cnfParse.set(cnfSec, "hidevscroll", str(self.hideVScroll))
cnfParse.set(cnfSec, "hidehscroll", str(self.hideHScroll))
## Project ## Project
cnfSec = "Project" cnfSec = "Project"
@@ -590,6 +611,8 @@ class Config:
cnfParse.set(cnfSec, "repdquotes", str(self.doReplaceDQuote)) cnfParse.set(cnfSec, "repdquotes", str(self.doReplaceDQuote))
cnfParse.set(cnfSec, "repdash", str(self.doReplaceDash)) cnfParse.set(cnfSec, "repdash", str(self.doReplaceDash))
cnfParse.set(cnfSec, "repdots", str(self.doReplaceDots)) 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, "fmtsinglequote", self._packList(self.fmtSingleQuotes))
cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes)) cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes))
cnfParse.set(cnfSec, "spelltool", str(self.spellTool)) cnfParse.set(cnfSec, "spelltool", str(self.spellTool))
+21 -2
View File
@@ -450,11 +450,19 @@ class GuiBuildNovel(QDialog):
# Tool Box Scroll Area # Tool Box Scroll Area
self.toolsArea = QScrollArea() self.toolsArea = QScrollArea()
self.toolsArea.setMinimumWidth(self.mainConf.pxInt(250)) self.toolsArea.setMinimumWidth(self.mainConf.pxInt(250))
self.toolsArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.toolsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.toolsArea.setWidgetResizable(True) self.toolsArea.setWidgetResizable(True)
self.toolsArea.setWidget(self.toolsWidget) 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 # Tools and Buttons Layout
self.innerBox = QVBoxLayout() self.innerBox = QVBoxLayout()
self.innerBox.addWidget(self.toolsArea) self.innerBox.addWidget(self.toolsArea)
@@ -1116,6 +1124,17 @@ class GuiBuildNovelDocView(QTextBrowser):
else: else:
self.setTabStopWidth(self.mainConf.getTabWidth()) self.setTabStopWidth(self.mainConf.getTabWidth())
# 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 = self.palette()
docPalette.setColor(QPalette.Base, QColor(255, 255, 255)) docPalette.setColor(QPalette.Base, QColor(255, 255, 255))
docPalette.setColor(QPalette.Text, QColor(0, 0, 0)) docPalette.setColor(QPalette.Text, QColor(0, 0, 0))
+66 -2
View File
@@ -88,6 +88,8 @@ class GuiDocEditor(QTextEdit):
self.bigDoc = False # Flag for very large document size self.bigDoc = False # Flag for very large document size
self.doReplace = False # Switch to temporarily disable auto-replace self.doReplace = False # Switch to temporarily disable auto-replace
self.queuePos = None # Used for delayed change of cursor position 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 # Typography
self.typDQOpen = self.mainConf.fmtDoubleQuotes[0] self.typDQOpen = self.mainConf.fmtDoubleQuotes[0]
@@ -100,6 +102,8 @@ class GuiDocEditor(QTextEdit):
self.qDocument.contentsChange.connect(self._docChange) self.qDocument.contentsChange.connect(self._docChange)
self.qDocument.documentLayout().documentSizeChanged.connect(self._docSizeChanged) self.qDocument.documentLayout().documentSizeChanged.connect(self._docSizeChanged)
self.verticalScrollBar().sliderMoved.connect(self._doVerticalScroll)
# Document Title # Document Title
self.docHeader = GuiDocEditHeader(self) self.docHeader = GuiDocEditHeader(self)
self.docFooter = GuiDocEditFooter(self) self.docFooter = GuiDocEditFooter(self)
@@ -224,6 +228,17 @@ class GuiDocEditor(QTextEdit):
self.qDocument.setDefaultTextOption(theOpt) self.qDocument.setDefaultTextOption(theOpt)
# 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 # Refresh the tab stops
if self.mainConf.verQtValue >= 51000: if self.mainConf.verQtValue >= 51000:
self.setTabStopDistance(self.mainConf.getTabWidth()) self.setTabStopDistance(self.mainConf.getTabWidth())
@@ -313,6 +328,7 @@ class GuiDocEditor(QTextEdit):
self.setCursorLine(tLine) self.setCursorLine(tLine)
self.docFooter.updateLineCount() self.docFooter.updateLineCount()
self.lengthLast = self.qDocument.characterCount()
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
@@ -360,9 +376,14 @@ class GuiDocEditor(QTextEdit):
return False return False
docText = self.getText() docText = self.getText()
cC, wC, pC = countWords(docText)
self._updateCounts(cC, wC, pC)
theItem.setCharCount(self.charCount) theItem.setCharCount(self.charCount)
theItem.setWordCount(self.wordCount) theItem.setWordCount(self.wordCount)
theItem.setParaCount(self.paraCount) theItem.setParaCount(self.paraCount)
self.saveCursorPosition() self.saveCursorPosition()
self.nwDocument.saveDocument(docText) self.nwDocument.saveDocument(docText)
self.setDocumentChanged(False) self.setDocumentChanged(False)
@@ -377,6 +398,7 @@ class GuiDocEditor(QTextEdit):
just ensure the margins are set correctly. just ensure the margins are set correctly.
""" """
wW = self.width() wW = self.width()
wH = self.height()
cM = self.mainConf.getTextMargin() cM = self.mainConf.getTextMargin()
vBar = self.verticalScrollBar() vBar = self.verticalScrollBar()
@@ -400,7 +422,7 @@ class GuiDocEditor(QTextEdit):
tW = wW - 2*tB - sW tW = wW - 2*tB - sW
tH = self.docHeader.height() tH = self.docHeader.height()
fH = self.docFooter.height() fH = self.docFooter.height()
fY = self.height() - fH - tB - sH fY = wH - fH - tB - sH
self.docHeader.setGeometry(tB, tB, tW, tH) self.docHeader.setGeometry(tB, tB, tW, tH)
self.docFooter.setGeometry(tB, fY, tW, fH) self.docFooter.setGeometry(tB, fY, tW, fH)
@@ -412,7 +434,14 @@ class GuiDocEditor(QTextEdit):
else: else:
rH = 0 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.scrollPastEnd:
docFrame = self.qDocument.rootFrame().frameFormat()
docFrame.setBottomMargin(wH - uM - lM - 4*tB - self.theTheme.fontPixelSize)
self.qDocument.rootFrame().setFrameFormat(docFrame)
return return
@@ -460,11 +489,14 @@ class GuiDocEditor(QTextEdit):
""" """
if not isinstance(thePosition, int): if not isinstance(thePosition, int):
return False return False
if thePosition >= 0: if thePosition >= 0:
theCursor = self.textCursor() theCursor = self.textCursor()
theCursor.setPosition(thePosition) theCursor.setPosition(thePosition)
self.setTextCursor(theCursor) self.setTextCursor(theCursor)
self.docFooter.updateLineCount() self.docFooter.updateLineCount()
self.cursorLast = self.cursorRect().center().y()
return True return True
def getCursorPosition(self): def getCursorPosition(self):
@@ -743,6 +775,22 @@ class GuiDocEditor(QTextEdit):
QTextEdit.keyPressEvent(self, keyEvent) QTextEdit.keyPressEvent(self, keyEvent)
self.docFooter.updateLineCount() 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 return
def focusNextPrevChild(self, toNext): def focusNextPrevChild(self, toNext):
@@ -768,9 +816,18 @@ class GuiDocEditor(QTextEdit):
QTextEdit.mouseReleaseEvent(self, mEvent) QTextEdit.mouseReleaseEvent(self, mEvent)
self.docFooter.updateLineCount() self.docFooter.updateLineCount()
self.cursorLast = self.cursorRect().center().y()
return 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): def resizeEvent(self, theEvent):
"""If the text editor is resize, we must make sure the document """If the text editor is resize, we must make sure the document
has its margins adjusted according to user preferences. has its margins adjusted according to user preferences.
@@ -805,6 +862,13 @@ class GuiDocEditor(QTextEdit):
self._docAutoReplace(self.qDocument.findBlock(thePos)) self._docAutoReplace(self.qDocument.findBlock(thePos))
return return
@pyqtSlot(int)
def _doVerticalScroll(self, theChange):
"""Update the cursor position on vertical scrolling.
"""
self.cursorLast = self.cursorRect().center().y()
return
@pyqtSlot("QPoint") @pyqtSlot("QPoint")
def _openContextMenu(self, thePos): def _openContextMenu(self, thePos):
"""Triggered by right click to open the context menu. Also """Triggered by right click to open the context menu. Also
+11
View File
@@ -124,6 +124,17 @@ class GuiDocViewer(QTextBrowser):
theOpt.setAlignment(Qt.AlignJustify) theOpt.setAlignment(Qt.AlignJustify)
self.qDocument.setDefaultTextOption(theOpt) self.qDocument.setDefaultTextOption(theOpt)
# 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 # Refresh the tab stops
if self.mainConf.verQtValue >= 51000: if self.mainConf.verQtValue >= 51000:
self.setTabStopDistance(self.mainConf.getTabWidth()) self.setTabStopDistance(self.mainConf.getTabWidth())
+17
View File
@@ -118,6 +118,7 @@ class GuiOutline(QTreeWidget):
self.colIndex = {} self.colIndex = {}
self.treeNCols = 0 self.treeNCols = 0
self.initOutline()
self.clearOutline() self.clearOutline()
self.headerMenu.setHiddenState(self.colHidden) self.headerMenu.setHiddenState(self.colHidden)
@@ -125,6 +126,22 @@ class GuiOutline(QTreeWidget):
return return
def initOutline(self):
"""Set or update outline settings.
"""
# 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
def clearOutline(self): def clearOutline(self):
"""Clear the tree and header and set the default values for the """Clear the tree and header and set the default values for the
columns arrays. columns arrays.
+18
View File
@@ -224,10 +224,28 @@ class GuiOutlineDetails(QScrollArea):
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWidgetResizable(True) self.setWidgetResizable(True)
self.initDetails()
logger.debug("GuiOutlineDetails initialisation complete") logger.debug("GuiOutlineDetails initialisation complete")
return return
def initDetails(self):
"""Set or update outline settings.
"""
# 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
def showItem(self, tHandle, sTitle): def showItem(self, tHandle, sTitle):
"""Update the content of the tree with the given handle and line """Update the content of the tree with the given handle and line
number pointing to a header. number pointing to a header.
+123 -40
View File
@@ -55,15 +55,17 @@ class GuiPreferences(PagedDialog):
self.setWindowTitle("Preferences") self.setWindowTitle("Preferences")
self.tabGeneral = GuiConfigEditGeneralTab(self.theParent) self.tabGeneral = GuiConfigEditGeneralTab(self.theParent)
self.tabLayout = GuiConfigEditLayoutTab(self.theParent) self.tabProjects = GuiConfigEditProjectsTab(self.theParent)
self.tabEditing = GuiConfigEditEditingTab(self.theParent) self.tabLayout = GuiConfigEditLayoutTab(self.theParent)
self.tabAutoRep = GuiConfigEditAutoReplaceTab(self.theParent) self.tabEditing = GuiConfigEditEditingTab(self.theParent)
self.tabAutoRep = GuiConfigEditAutoReplaceTab(self.theParent)
self.addTab(self.tabGeneral, "General") self.addTab(self.tabGeneral, "General")
self.addTab(self.tabLayout, "Text Layout") self.addTab(self.tabProjects, "Projects")
self.addTab(self.tabEditing, "Editor") self.addTab(self.tabLayout, "Text Layout")
self.addTab(self.tabAutoRep, "Auto-Replace") self.addTab(self.tabEditing, "Editor")
self.addTab(self.tabAutoRep, "Auto-Replace")
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doSave) self.buttonBox.accepted.connect(self._doSave)
@@ -91,6 +93,10 @@ class GuiPreferences(PagedDialog):
validEntries &= retA validEntries &= retA
needsRestart |= retB needsRestart |= retB
retA, retB = self.tabProjects.saveValues()
validEntries &= retA
needsRestart |= retB
retA, retB = self.tabLayout.saveValues() retA, retB = self.tabLayout.saveValues()
validEntries &= retA validEntries &= retA
needsRestart |= retB needsRestart |= retB
@@ -219,9 +225,94 @@ class GuiConfigEditGeneralTab(QWidget):
self.showFullPath.setChecked(self.mainConf.showFullPath) self.showFullPath.setChecked(self.mainConf.showFullPath)
self.mainForm.addRow( self.mainForm.addRow(
"Show full path in document header", "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
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()
hideVScroll = self.hideVScroll.isChecked()
hideHScroll = self.hideHScroll.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.hideVScroll = hideVScroll
self.mainConf.hideHScroll = hideHScroll
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 # AutoSave Settings
# ================= # =================
self.mainForm.addGroupLabel("Automatic Save") self.mainForm.addGroupLabel("Automatic Save")
@@ -292,30 +383,12 @@ class GuiConfigEditGeneralTab(QWidget):
validEntries = True validEntries = True
needsRestart = False 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() autoSaveDoc = self.autoSaveDoc.value()
autoSaveProj = self.autoSaveProj.value() autoSaveProj = self.autoSaveProj.value()
backupPath = self.backupPath backupPath = self.backupPath
backupOnClose = self.backupOnClose.isChecked() backupOnClose = self.backupOnClose.isChecked()
askBeforeBackup = self.askBeforeBackup.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.autoSaveDoc = autoSaveDoc
self.mainConf.autoSaveProj = autoSaveProj self.mainConf.autoSaveProj = autoSaveProj
self.mainConf.backupPath = backupPath self.mainConf.backupPath = backupPath
@@ -357,19 +430,7 @@ class GuiConfigEditGeneralTab(QWidget):
self.askBeforeBackup.setEnabled(theState) self.askBeforeBackup.setEnabled(theState)
return return
def _selectFont(self): # END Class GuiConfigEditProjectsTab
"""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 GuiConfigEditLayoutTab(QWidget): class GuiConfigEditLayoutTab(QWidget):
@@ -494,6 +555,24 @@ class GuiConfigEditLayoutTab(QWidget):
theUnit="px" 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 return
def saveValues(self): def saveValues(self):
@@ -511,6 +590,8 @@ class GuiConfigEditLayoutTab(QWidget):
doJustify = self.textJustify.isChecked() doJustify = self.textJustify.isChecked()
textMargin = self.textMargin.value() textMargin = self.textMargin.value()
tabWidth = self.tabWidth.value() tabWidth = self.tabWidth.value()
scrollPastEnd = self.scrollPastEnd.isChecked()
scollWithCursor = self.scollWithCursor.isChecked()
self.mainConf.textFont = textFont self.mainConf.textFont = textFont
self.mainConf.textSize = textSize self.mainConf.textSize = textSize
@@ -521,6 +602,8 @@ class GuiConfigEditLayoutTab(QWidget):
self.mainConf.doJustify = doJustify self.mainConf.doJustify = doJustify
self.mainConf.textMargin = textMargin self.mainConf.textMargin = textMargin
self.mainConf.tabWidth = tabWidth self.mainConf.tabWidth = tabWidth
self.mainConf.scrollPastEnd = scrollPastEnd
self.mainConf.scollWithCursor = scollWithCursor
self.mainConf.confChanged = True self.mainConf.confChanged = True
+21 -1
View File
@@ -115,6 +115,9 @@ class GuiProjectTree(QTreeWidget):
# The last column should just auto-scale # The last column should just auto-scale
self.resizeColumnToContents(self.C_FLAGS) self.resizeColumnToContents(self.C_FLAGS)
# Set custom settings
self.initTree()
logger.debug("GuiProjectTree initialisation complete") logger.debug("GuiProjectTree initialisation complete")
# Internal Mapping # Internal Mapping
@@ -122,6 +125,22 @@ class GuiProjectTree(QTreeWidget):
return return
def initTree(self):
"""Set or update tree widget settings.
"""
# 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
## ##
# Class Methods # Class Methods
## ##
@@ -241,7 +260,8 @@ class GuiProjectTree(QTreeWidget):
# Add the new item to the tree # Add the new item to the tree
if tHandle is not None: if tHandle is not None:
self.revealNewTreeItem(tHandle, nHandle) self.revealNewTreeItem(tHandle, nHandle)
self.theParent.editItem(tHandle) if self.mainConf.showGUI:
self.theParent.editItem(tHandle)
return True return True
+3
View File
@@ -790,6 +790,9 @@ class GuiMain(QMainWindow):
self.saveDocument() self.saveDocument()
self.docEditor.initEditor() self.docEditor.initEditor()
self.docViewer.initViewer() self.docViewer.initViewer()
self.treeView.initTree()
self.projView.initOutline()
self.projMeta.initDetails()
return return
+5 -1
View File
@@ -1,5 +1,5 @@
[Main] [Main]
timestamp = 2020-06-29 17:34:15 timestamp = 2020-10-11 18:29:34
theme = default theme = default
syntax = default_light syntax = default_light
icons = typicons_colour_light icons = typicons_colour_light
@@ -16,6 +16,8 @@ docpane = 400, 400
viewpane = 500, 150 viewpane = 500, 150
outlinepane = 500, 150 outlinepane = 500, 150
fullscreen = False fullscreen = False
hidevscroll = False
hidehscroll = False
[Project] [Project]
autosaveproject = 60 autosaveproject = 60
@@ -37,6 +39,8 @@ repsquotes = True
repdquotes = True repdquotes = True
repdash = True repdash = True
repdots = True repdots = True
scrollpastend = True
scollwithcursor = False
fmtsinglequote = , fmtsinglequote = ,
fmtdoublequote = “, ” fmtdoublequote = “, ”
spelltool = internal spelltool = internal
+4
View File
@@ -16,6 +16,8 @@ docpane = 400, 400
viewpane = 500, 150 viewpane = 500, 150
outlinepane = 500, 150 outlinepane = 500, 150
fullscreen = False fullscreen = False
hidevscroll = True
hidehscroll = True
[Project] [Project]
autosaveproject = 40 autosaveproject = 40
@@ -37,6 +39,8 @@ repsquotes = True
repdquotes = True repdquotes = True
repdash = True repdash = True
repdots = True repdots = True
scrollpastend = False
scollwithcursor = True
fmtsinglequote = , fmtsinglequote = ,
fmtdoublequote = “, ” fmtdoublequote = “, ”
spelltool = internal spelltool = internal
+73 -69
View File
@@ -23,7 +23,7 @@ from nw.gui import (
GuiProjectLoad, GuiPreferences GuiProjectLoad, GuiPreferences
) )
from nw.gui.custom import QuotesDialog from nw.gui.custom import QuotesDialog
from nw.constants import nwItemType, nwItemLayout, nwItemClass from nw.constants import nwItemLayout, nwItemClass, nwFiles
keyDelay = 2 keyDelay = 2
typeDelay = 1 typeDelay = 1
@@ -228,48 +228,20 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
# Create new, save, close project # Create new, save, close project
nwGUI.theProject.projTree.setSeed(42) nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": nwFuncTemp}) assert nwGUI.newProject({"projPath": nwFuncTemp})
qtbot.wait(200)
assert nwGUI.saveProject() assert nwGUI.saveProject()
assert nwGUI.closeProject() assert nwGUI.closeProject()
qtbot.wait(stepDelay) qtbot.wait(stepDelay)
# Check that we cannot open when there is no project sessFile = os.path.join(nwFuncTemp, "meta", nwFiles.SESS_STATS)
nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger) with open(sessFile, mode="w+", encoding="utf-8") as outFile:
assert getGuiItem("GuiWritingStats") is None outFile.write(
"# Start Time End Time Novel Notes\n"
assert nwGUI.openProject(nwFuncTemp) "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n"
qtbot.wait(stepDelay) "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"
# Add some text to the scene file "2020-01-06 21:00:00 2020-01-06 21:00:10 125 5\n"
assert nwGUI.openDocument("0e17daca5f3e1") )
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"
)
assert nwGUI.saveDocument()
# Add a note file with some text
nwGUI.setFocus(1)
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"
)
assert nwGUI.saveDocument()
qtbot.wait(500) # Ensures that the session length is > 0
assert nwGUI.saveProject()
assert nwGUI.closeProject()
qtbot.wait(stepDelay)
# Open again, and check the stats # Open again, and check the stats
assert nwGUI.openProject(nwFuncTemp) assert nwGUI.openProject(nwFuncTemp)
@@ -288,19 +260,21 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, "")) monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, ""))
assert sessLog._saveData(sessLog.FMT_CSV) assert sessLog._saveData(sessLog.FMT_CSV)
qtbot.wait(stepDelay) qtbot.wait(100)
assert sessLog._saveData(sessLog.FMT_JSON) assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay) qtbot.wait(100)
jsonStats = os.path.join(nwFuncTemp, "sessionStats.json") jsonStats = os.path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read()) jsonData = json.loads(inFile.read())
assert len(jsonData) == 2 qtbot.wait(stepDelay)
assert jsonData[1]["length"] >= 0
assert jsonData[1]["newWords"] == 126 assert len(jsonData) == 3
assert jsonData[1]["novelWords"] == 127 assert jsonData[1]["length"] >= 14.0
assert jsonData[1]["noteWords"] == 5 assert jsonData[1]["newWords"] == 119
assert jsonData[1]["novelWords"] == 125
assert jsonData[1]["noteWords"] == 0
# No Novel Files # No Novel Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
@@ -313,9 +287,9 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
jsonData = json.loads(inFile.read()) jsonData = json.loads(inFile.read())
assert len(jsonData) == 1 assert len(jsonData) == 1
assert jsonData[0]["length"] >= 0 assert jsonData[0]["length"] >= 14.0
assert jsonData[0]["newWords"] == 5 assert jsonData[0]["newWords"] == 5
assert jsonData[0]["novelWords"] == 127 assert jsonData[0]["novelWords"] == 125
assert jsonData[0]["noteWords"] == 5 assert jsonData[0]["noteWords"] == 5
# No Note Files # No Note Files
@@ -330,10 +304,10 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
jsonData = json.loads(inFile.read()) jsonData = json.loads(inFile.read())
assert len(jsonData) == 2 assert len(jsonData) == 2
assert jsonData[1]["length"] >= 0 assert jsonData[1]["length"] >= 14.0
assert jsonData[1]["newWords"] == 121 assert jsonData[1]["newWords"] == 119
assert jsonData[1]["novelWords"] == 127 assert jsonData[1]["novelWords"] == 125
assert jsonData[1]["noteWords"] == 5 assert jsonData[1]["noteWords"] == 0
# No Negative Entries # No Negative Entries
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
@@ -346,7 +320,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read()) jsonData = json.loads(inFile.read())
assert len(jsonData) == 2 assert len(jsonData) == 3
# Un-hide Zero Entries # Un-hide Zero Entries
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
@@ -359,7 +333,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read()) jsonData = json.loads(inFile.read())
assert len(jsonData) == 2 assert len(jsonData) == 4
# Group by Day # Group by Day
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
@@ -373,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. # 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 # A failed test should in any case produce a 4
assert len(jsonData) in (1, 2) assert len(jsonData) == 3
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
sessLog._doClose() sessLog._doClose()
assert nwGUI.closeProject()
qtbot.wait(stepDelay)
nwGUI.closeMain() nwGUI.closeMain()
@pytest.mark.gui @pytest.mark.gui
@@ -1057,6 +1033,7 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
nwGUI.mainConf = tmpConf nwGUI.mainConf = tmpConf
nwPrefs.mainConf = tmpConf nwPrefs.mainConf = tmpConf
nwPrefs.tabGeneral.mainConf = tmpConf nwPrefs.tabGeneral.mainConf = tmpConf
nwPrefs.tabProjects.mainConf = tmpConf
nwPrefs.tabLayout.mainConf = tmpConf nwPrefs.tabLayout.mainConf = tmpConf
nwPrefs.tabEditing.mainConf = tmpConf nwPrefs.tabEditing.mainConf = tmpConf
nwPrefs.tabAutoRep.mainConf = tmpConf nwPrefs.tabAutoRep.mainConf = tmpConf
@@ -1065,7 +1042,6 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
tabGeneral = nwPrefs.tabGeneral tabGeneral = nwPrefs.tabGeneral
nwPrefs._tabBox.setCurrentWidget(tabGeneral) nwPrefs._tabBox.setCurrentWidget(tabGeneral)
tabGeneral.backupPath = "no/where"
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
assert not tabGeneral.preferDarkIcons.isChecked() assert not tabGeneral.preferDarkIcons.isChecked()
@@ -1077,27 +1053,45 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton) qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
assert not tabGeneral.showFullPath.isChecked() assert not tabGeneral.showFullPath.isChecked()
# Check Browse button qtbot.wait(keyDelay)
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "") assert not tabGeneral.hideVScroll.isChecked()
assert not tabGeneral._backupFolder() qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "some/dir") assert tabGeneral.hideVScroll.isChecked()
qtbot.mouseClick(tabGeneral.backupGetPath, Qt.LeftButton)
qtbot.wait(keyDelay)
assert not tabGeneral.hideHScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
assert tabGeneral.hideHScroll.isChecked()
# Check font button # Check font button
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True)) monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton) qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
assert not tabGeneral.backupOnClose.isChecked() tabGeneral.guiFontSize.setValue(12)
qtbot.mouseClick(tabGeneral.backupOnClose, Qt.LeftButton)
assert tabGeneral.backupOnClose.isChecked() # Projects Settings
qtbot.wait(keyDelay)
tabProjects = nwPrefs.tabProjects
nwPrefs._tabBox.setCurrentWidget(tabProjects)
tabProjects.backupPath = "no/where"
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
tabGeneral.guiFontSize.setValue(12) assert not tabProjects.backupOnClose.isChecked()
tabGeneral.autoSaveDoc.setValue(20) qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
tabGeneral.autoSaveProj.setValue(40) 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) qtbot.wait(keyDelay)
tabLayout = nwPrefs.tabLayout tabLayout = nwPrefs.tabLayout
nwPrefs._tabBox.setCurrentWidget(tabLayout) nwPrefs._tabBox.setCurrentWidget(tabLayout)
@@ -1127,6 +1121,16 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
qtbot.mouseClick(tabLayout.textJustify, Qt.LeftButton) qtbot.mouseClick(tabLayout.textJustify, Qt.LeftButton)
assert not tabLayout.textJustify.isChecked() 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 # Editor Settings
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
tabEditing = nwPrefs.tabEditing tabEditing = nwPrefs.tabEditing
@@ -1198,7 +1202,7 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
ignoreLines = [ ignoreLines = [
2, # Timestamp 2, # Timestamp
11, 12, 13, 14, 15, 16, 17, # Window sizes 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) assert cmpFiles(testConf, refConf, ignoreLines)