@@ -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
|
||||
@@ -122,6 +126,9 @@ class Config:
|
||||
self.doReplaceDQuote = True
|
||||
self.doReplaceDash = True
|
||||
self.doReplaceDots = True
|
||||
self.scrollPastEnd = True
|
||||
self.scollWithCursor = False
|
||||
|
||||
self.wordCountTimer = 5.0
|
||||
self.showTabsNSpaces = False
|
||||
self.showLineEndings = False
|
||||
@@ -391,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"
|
||||
@@ -448,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
|
||||
)
|
||||
@@ -565,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"
|
||||
@@ -590,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))
|
||||
|
||||
+21
-2
@@ -450,11 +450,19 @@ 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)
|
||||
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()
|
||||
self.innerBox.addWidget(self.toolsArea)
|
||||
@@ -1116,6 +1124,17 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
else:
|
||||
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.setColor(QPalette.Base, QColor(255, 255, 255))
|
||||
docPalette.setColor(QPalette.Text, QColor(0, 0, 0))
|
||||
|
||||
+66
-2
@@ -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)
|
||||
@@ -224,6 +228,17 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
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
|
||||
if self.mainConf.verQtValue >= 51000:
|
||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||
@@ -313,6 +328,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.setCursorLine(tLine)
|
||||
|
||||
self.docFooter.updateLineCount()
|
||||
self.lengthLast = self.qDocument.characterCount()
|
||||
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
@@ -360,9 +376,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)
|
||||
@@ -377,6 +398,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 +422,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 +434,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.scrollPastEnd:
|
||||
docFrame = self.qDocument.rootFrame().frameFormat()
|
||||
docFrame.setBottomMargin(wH - uM - lM - 4*tB - self.theTheme.fontPixelSize)
|
||||
self.qDocument.rootFrame().setFrameFormat(docFrame)
|
||||
|
||||
return
|
||||
|
||||
@@ -460,11 +489,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):
|
||||
@@ -743,6 +775,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):
|
||||
@@ -768,9 +816,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.
|
||||
@@ -805,6 +862,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
|
||||
|
||||
@@ -124,6 +124,17 @@ class GuiDocViewer(QTextBrowser):
|
||||
theOpt.setAlignment(Qt.AlignJustify)
|
||||
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
|
||||
if self.mainConf.verQtValue >= 51000:
|
||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||
|
||||
@@ -118,6 +118,7 @@ class GuiOutline(QTreeWidget):
|
||||
self.colIndex = {}
|
||||
self.treeNCols = 0
|
||||
|
||||
self.initOutline()
|
||||
self.clearOutline()
|
||||
self.headerMenu.setHiddenState(self.colHidden)
|
||||
|
||||
@@ -125,6 +126,22 @@ class GuiOutline(QTreeWidget):
|
||||
|
||||
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):
|
||||
"""Clear the tree and header and set the default values for the
|
||||
columns arrays.
|
||||
|
||||
@@ -224,10 +224,28 @@ 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)
|
||||
else:
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
|
||||
if self.mainConf.hideHScroll:
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
else:
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
|
||||
return
|
||||
|
||||
def showItem(self, tHandle, sTitle):
|
||||
"""Update the content of the tree with the given handle and line
|
||||
number pointing to a header.
|
||||
|
||||
+123
-40
@@ -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
|
||||
@@ -219,9 +225,94 @@ 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
|
||||
|
||||
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
|
||||
# =================
|
||||
self.mainForm.addGroupLabel("Automatic Save")
|
||||
@@ -292,30 +383,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 +430,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):
|
||||
|
||||
@@ -494,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):
|
||||
@@ -511,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
|
||||
@@ -521,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
|
||||
|
||||
|
||||
+21
-1
@@ -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,22 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
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
|
||||
##
|
||||
@@ -241,7 +260,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,6 +16,8 @@ docpane = 400, 400
|
||||
viewpane = 500, 150
|
||||
outlinepane = 500, 150
|
||||
fullscreen = False
|
||||
hidevscroll = True
|
||||
hidehscroll = True
|
||||
|
||||
[Project]
|
||||
autosaveproject = 40
|
||||
@@ -37,6 +39,8 @@ repsquotes = True
|
||||
repdquotes = True
|
||||
repdash = True
|
||||
repdots = True
|
||||
scrollpastend = False
|
||||
scollwithcursor = True
|
||||
fmtsinglequote = ‘, ’
|
||||
fmtdoublequote = “, ”
|
||||
spelltool = internal
|
||||
|
||||
+73
-69
@@ -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 nwItemLayout, nwItemClass, nwFiles
|
||||
|
||||
keyDelay = 2
|
||||
typeDelay = 1
|
||||
@@ -228,48 +228,20 @@ 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)
|
||||
|
||||
# Check that we cannot open when there is no project
|
||||
nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger)
|
||||
assert getGuiItem("GuiWritingStats") is None
|
||||
|
||||
assert nwGUI.openProject(nwFuncTemp)
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
# Add some text to the scene file
|
||||
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)
|
||||
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)
|
||||
@@ -288,19 +260,21 @@ 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())
|
||||
|
||||
assert len(jsonData) == 2
|
||||
assert jsonData[1]["length"] >= 0
|
||||
assert jsonData[1]["newWords"] == 126
|
||||
assert jsonData[1]["novelWords"] == 127
|
||||
assert jsonData[1]["noteWords"] == 5
|
||||
qtbot.wait(stepDelay)
|
||||
|
||||
assert len(jsonData) == 3
|
||||
assert jsonData[1]["length"] >= 14.0
|
||||
assert jsonData[1]["newWords"] == 119
|
||||
assert jsonData[1]["novelWords"] == 125
|
||||
assert jsonData[1]["noteWords"] == 0
|
||||
|
||||
# No Novel Files
|
||||
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
|
||||
@@ -313,9 +287,9 @@ 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"] == 127
|
||||
assert jsonData[0]["novelWords"] == 125
|
||||
assert jsonData[0]["noteWords"] == 5
|
||||
|
||||
# No Note Files
|
||||
@@ -330,10 +304,10 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
|
||||
jsonData = json.loads(inFile.read())
|
||||
|
||||
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]["length"] >= 14.0
|
||||
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 +320,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 +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) == 2
|
||||
assert len(jsonData) == 4
|
||||
|
||||
# Group by Day
|
||||
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.
|
||||
# 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
|
||||
@@ -1057,6 +1033,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 +1042,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 +1053,45 @@ 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)
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -1127,6 +1121,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
|
||||
@@ -1198,7 +1202,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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user