Merge pull request #482 from vkbo/typewriter_mode

Updated Typewriter Mode
This commit is contained in:
Veronica K. Berglyd Olsen
2020-10-18 20:06:31 +02:00
committed by GitHub
8 changed files with 112 additions and 89 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# novelWriter ChangeLog # novelWriter ChangeLog
## Version 1.0 Release Beta 5 [2020-10-18] ## Version 1.0 Beta 5 [2020-10-18]
**Important Notes** **Important Notes**
+56 -43
View File
@@ -107,44 +107,52 @@ class Config:
self.hideHScroll = False # Hide horizontal scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets
## Project ## Project
self.autoSaveProj = 60 self.autoSaveProj = 60 # Interval for auto-saving project in seconds
self.autoSaveDoc = 30 self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
## Text Editor ## Text Editor
self.textFont = None self.textFont = None # Editor font
self.textSize = 12 self.textSize = 12 # Editor font size
self.textFixedW = True self.textFixedW = True # Keep editor text fixed width
self.textWidth = 600 self.textWidth = 600 # Editor text width
self.textMargin = 40 self.textMargin = 40 # Editor/viewer text margin
self.tabWidth = 40 self.tabWidth = 40 # Editor tabulator width
self.focusWidth = 800
self.hideFocusFooter = False
self.doJustify = False
self.autoSelect = True
self.doReplace = True
self.doReplaceSQuote = True
self.doReplaceDQuote = True
self.doReplaceDash = True
self.doReplaceDots = True
self.scrollPastEnd = True
self.scollWithCursor = False
self.scollToPoint = 40
self.wordCountTimer = 5.0 self.focusWidth = 800 # Focus Mode text width
self.showTabsNSpaces = False self.hideFocusFooter = False # Hide document footer in Focus Mode
self.showLineEndings = False self.showFullPath = True # Show full document path in editor header
self.bigDocLimit = 800 self.autoSelect = True # Auto-select word when applying format with no selection
self.showFullPath = True
self.highlightQuotes = True
self.highlightEmph = True
self.doJustify = False # Justify text
self.showTabsNSpaces = False # Show tabs and spaces in edior
self.showLineEndings = False # Show line endings in editor
self.doReplace = True # Enable auto-replace as you type
self.doReplaceSQuote = True # Smart single quotes
self.doReplaceDQuote = True # Smart double quotes
self.doReplaceDash = True # Replace multiple hyphens with dashes
self.doReplaceDots = True # Replace three dots with ellipsis
self.scrollPastEnd = True # Allow scrolling past end of document
self.autoScroll = False # Typewriter-like scrolling
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
self.wordCountTimer = 5.0 # Interval for word count update in seconds
self.bigDocLimit = 800 # Size threshold for heavy editor features in kilobytes
self.highlightQuotes = True # Highlight text in quotes
self.highlightEmph = True # Add colour to text emphasis
## User-Selected Symbols
self.fmtApostrophe = nwUnicode.U_RSQUO self.fmtApostrophe = nwUnicode.U_RSQUO
self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO] self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO]
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO] self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO]
## Spell Checking
self.spellTool = None self.spellTool = None
self.spellLanguage = None self.spellLanguage = None
## Search Bar Switches
self.searchCase = False self.searchCase = False
self.searchWord = False self.searchWord = False
self.searchRegEx = False self.searchRegEx = False
@@ -465,11 +473,11 @@ class Config:
self.scrollPastEnd = self._parseLine( self.scrollPastEnd = self._parseLine(
cnfParse, cnfSec, "scrollpastend", self.CNF_BOOL, self.scrollPastEnd cnfParse, cnfSec, "scrollpastend", self.CNF_BOOL, self.scrollPastEnd
) )
self.scollWithCursor = self._parseLine( self.autoScroll = self._parseLine(
cnfParse, cnfSec, "scollwithcursor", self.CNF_BOOL, self.scollWithCursor cnfParse, cnfSec, "autoscroll", self.CNF_BOOL, self.autoScroll
) )
self.scollToPoint = self._parseLine( self.autoScrollPos = self._parseLine(
cnfParse, cnfSec, "scolltopoint", self.CNF_INT, self.scollToPoint cnfParse, cnfSec, "autoscrollpos", self.CNF_INT, self.autoScrollPos
) )
self.fmtSingleQuotes = self._parseLine( self.fmtSingleQuotes = self._parseLine(
cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes
@@ -616,8 +624,8 @@ class Config:
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, "scrollpastend", str(self.scrollPastEnd))
cnfParse.set(cnfSec, "scollwithcursor", str(self.scollWithCursor)) cnfParse.set(cnfSec, "autoscroll", str(self.autoScroll))
cnfParse.set(cnfSec, "scolltopoint", str(self.scollToPoint)) cnfParse.set(cnfSec, "autoscrollpos", str(self.autoScrollPos))
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))
@@ -911,16 +919,21 @@ class Config:
""" """
if cnfParse.has_section(cnfSec): if cnfParse.has_section(cnfSec):
if cnfParse.has_option(cnfSec, cnfName): if cnfParse.has_option(cnfSec, cnfName):
if cnfType == self.CNF_STR: try:
return cnfParse.get(cnfSec, cnfName) if cnfType == self.CNF_STR:
elif cnfType == self.CNF_INT: return cnfParse.get(cnfSec, cnfName)
return cnfParse.getint(cnfSec, cnfName) elif cnfType == self.CNF_INT:
elif cnfType == self.CNF_BOOL: return cnfParse.getint(cnfSec, cnfName)
return cnfParse.getboolean(cnfSec, cnfName) elif cnfType == self.CNF_BOOL:
elif cnfType == self.CNF_LIST: return cnfParse.getboolean(cnfSec, cnfName)
return self._unpackList( elif cnfType == self.CNF_LIST:
cnfParse.get(cnfSec, cnfName), len(cnfDefault), cnfDefault return self._unpackList(
) cnfParse.get(cnfSec, cnfName), len(cnfDefault), cnfDefault
)
except ValueError as e:
logger.error("Failed to load value from config file.")
logger.error(str(e))
return cnfDefault return cnfDefault
def _checkNone(self, checkVal): def _checkNone(self, checkVal):
+32 -22
View File
@@ -61,6 +61,11 @@ logger = logging.getLogger(__name__)
class GuiDocEditor(QTextEdit): class GuiDocEditor(QTextEdit):
MOVE_KEYS = (
Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down,
Qt.Key_PageUp, Qt.Key_PageDown
)
def __init__(self, theParent): def __init__(self, theParent):
QTextEdit.__init__(self, theParent) QTextEdit.__init__(self, theParent)
@@ -436,8 +441,7 @@ class GuiDocEditor(QTextEdit):
if self.mainConf.scrollPastEnd: if self.mainConf.scrollPastEnd:
docFrame = self.qDocument.rootFrame().frameFormat() docFrame = self.qDocument.rootFrame().frameFormat()
docMargin = wH - uM - lM - 4*tB - 5*self.theTheme.fontPixelSize docFrame.setBottomMargin(max(0, 0.6*(wH - uM - lM - 4*tB)))
docFrame.setBottomMargin(max(0, docMargin))
self.qDocument.rootFrame().setFrameFormat(docFrame) self.qDocument.rootFrame().setFrameFormat(docFrame)
return return
@@ -765,33 +769,39 @@ class GuiDocEditor(QTextEdit):
return return
elif keyEvent == QKeySequence.Redo: elif keyEvent == QKeySequence.Redo:
self.docAction(nwDocAction.REDO) self.docAction(nwDocAction.REDO)
return
elif keyEvent == QKeySequence.Undo: elif keyEvent == QKeySequence.Undo:
self.docAction(nwDocAction.UNDO) self.docAction(nwDocAction.UNDO)
return
elif keyEvent == QKeySequence.SelectAll: elif keyEvent == QKeySequence.SelectAll:
self.docAction(nwDocAction.SEL_ALL) self.docAction(nwDocAction.SEL_ALL)
return
if self.mainConf.autoScroll:
cOld = self.cursorRect().center().y()
QTextEdit.keyPressEvent(self, keyEvent)
kMod = keyEvent.modifiers()
okMod = kMod == Qt.NoModifier or kMod == Qt.ShiftModifier
okKey = keyEvent.key() not in self.MOVE_KEYS
if okMod and okKey:
cNew = self.cursorRect().center().y()
cMov = cNew - cOld
mPos = self.mainConf.autoScrollPos * self.viewport().height() * 0.01
if abs(cMov) > 0 and cOld > mPos:
# Move the scroll bar
vBar = self.verticalScrollBar()
doAnim = QPropertyAnimation(vBar, b"value", self)
doAnim.setDuration(120)
doAnim.setStartValue(vBar.value())
doAnim.setEndValue(vBar.value() + cMov)
doAnim.start()
else: else:
QTextEdit.keyPressEvent(self, keyEvent) QTextEdit.keyPressEvent(self, keyEvent)
self.docFooter.updateLineCount()
if self.mainConf.scollWithCursor: self.docFooter.updateLineCount()
kMod = keyEvent.modifiers()
if kMod == Qt.NoModifier or kMod == Qt.ShiftModifier:
hWid = self.viewport().height()
cPos = self.cursorRect().center().y()
mPos = self.mainConf.scollToPoint * hWid
vBar = self.verticalScrollBar()
# Compute the needed scroll and duration
pOld = vBar.value()
pNew = pOld + cPos - round(mPos*0.01)
aDur = 150 + round(min(abs(pNew - pOld)/hWid, 1.0)*500)
if pNew >= 0:
doAnim = QPropertyAnimation(vBar, b"value", self)
doAnim.setDuration(aDur)
doAnim.setStartValue(pOld)
doAnim.setEndValue(pNew)
doAnim.start()
return return
+14 -14
View File
@@ -569,23 +569,23 @@ class GuiConfigEditLayoutTab(QWidget):
) )
## Typewriter Scrolling ## Typewriter Scrolling
self.scollWithCursor = QSwitch() self.autoScroll = QSwitch()
self.scollWithCursor.setChecked(self.mainConf.scollWithCursor) self.autoScroll.setChecked(self.mainConf.autoScroll)
self.mainForm.addRow( self.mainForm.addRow(
"Typewriter style scrolling when you type", "Typewriter style scrolling when you type",
self.scollWithCursor, self.autoScroll,
"Tries to keep the cursor at a fixed vertical position." "Tries to keep the cursor at a fixed vertical position."
) )
## Font Size ## Font Size
self.scollToPoint = QSpinBox(self) self.autoScrollPos = QSpinBox(self)
self.scollToPoint.setMinimum(10) self.autoScrollPos.setMinimum(10)
self.scollToPoint.setMaximum(90) self.autoScrollPos.setMaximum(90)
self.scollToPoint.setSingleStep(1) self.autoScrollPos.setSingleStep(1)
self.scollToPoint.setValue(self.mainConf.scollToPoint) self.autoScrollPos.setValue(int(self.mainConf.autoScrollPos))
self.mainForm.addRow( self.mainForm.addRow(
"Position in the editor to keep the cursor", "Minimum position for Typewriter scrolling",
self.scollToPoint, self.autoScrollPos,
"In units of percentage of the editor height.", "In units of percentage of the editor height.",
theUnit = "%" theUnit = "%"
) )
@@ -608,8 +608,8 @@ class GuiConfigEditLayoutTab(QWidget):
textMargin = self.textMargin.value() textMargin = self.textMargin.value()
tabWidth = self.tabWidth.value() tabWidth = self.tabWidth.value()
scrollPastEnd = self.scrollPastEnd.isChecked() scrollPastEnd = self.scrollPastEnd.isChecked()
scollWithCursor = self.scollWithCursor.isChecked() autoScroll = self.autoScroll.isChecked()
scollToPoint = self.scollToPoint.value() autoScrollPos = self.autoScrollPos.value()
self.mainConf.textFont = textFont self.mainConf.textFont = textFont
self.mainConf.textSize = textSize self.mainConf.textSize = textSize
@@ -621,8 +621,8 @@ class GuiConfigEditLayoutTab(QWidget):
self.mainConf.textMargin = textMargin self.mainConf.textMargin = textMargin
self.mainConf.tabWidth = tabWidth self.mainConf.tabWidth = tabWidth
self.mainConf.scrollPastEnd = scrollPastEnd self.mainConf.scrollPastEnd = scrollPastEnd
self.mainConf.scollWithCursor = scollWithCursor self.mainConf.autoScroll = autoScroll
self.mainConf.scollToPoint = scollToPoint self.mainConf.autoScrollPos = autoScrollPos
self.mainConf.confChanged = True self.mainConf.confChanged = True
+2 -2
View File
@@ -40,8 +40,8 @@ repdquotes = True
repdash = True repdash = True
repdots = True repdots = True
scrollpastend = True scrollpastend = True
scollwithcursor = False autoscroll = False
scolltopoint = 40 autoscrollpos = 30
fmtsinglequote = , fmtsinglequote = ,
fmtdoublequote = “, ” fmtdoublequote = “, ”
spelltool = internal spelltool = internal
+2 -2
View File
@@ -40,8 +40,8 @@ repdquotes = True
repdash = True repdash = True
repdots = True repdots = True
scrollpastend = False scrollpastend = False
scollwithcursor = True autoscroll = True
scolltopoint = 40 autoscrollpos = 30
fmtsinglequote = , fmtsinglequote = ,
fmtdoublequote = “, ” fmtdoublequote = “, ”
spelltool = internal spelltool = internal
+3 -3
View File
@@ -1127,9 +1127,9 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
assert not tabLayout.scrollPastEnd.isChecked() assert not tabLayout.scrollPastEnd.isChecked()
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
assert not tabLayout.scollWithCursor.isChecked() assert not tabLayout.autoScroll.isChecked()
qtbot.mouseClick(tabLayout.scollWithCursor, Qt.LeftButton) qtbot.mouseClick(tabLayout.autoScroll, Qt.LeftButton)
assert tabLayout.scollWithCursor.isChecked() assert tabLayout.autoScroll.isChecked()
# Editor Settings # Editor Settings
qtbot.wait(keyDelay) qtbot.wait(keyDelay)
+2 -2
View File
@@ -172,8 +172,8 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
nwGUI.mainConf.hideHScroll = True nwGUI.mainConf.hideHScroll = True
nwGUI.mainConf.hideVScroll = True nwGUI.mainConf.hideVScroll = True
nwGUI.mainConf.scrollPastEnd = True nwGUI.mainConf.scrollPastEnd = True
nwGUI.mainConf.scollToPoint = 80 nwGUI.mainConf.autoScrollPos = 80
nwGUI.mainConf.scollWithCursor = True nwGUI.mainConf.autoScroll = True
# Add a Character File # Add a Character File
nwGUI.setFocus(1) nwGUI.setFocus(1)