Merge pull request #482 from vkbo/typewriter_mode
Updated Typewriter Mode
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
# novelWriter ChangeLog
|
||||
|
||||
## Version 1.0 Release Beta 5 [2020-10-18]
|
||||
## Version 1.0 Beta 5 [2020-10-18]
|
||||
|
||||
**Important Notes**
|
||||
|
||||
|
||||
+56
-43
@@ -107,44 +107,52 @@ class Config:
|
||||
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
|
||||
|
||||
## Project
|
||||
self.autoSaveProj = 60
|
||||
self.autoSaveDoc = 30
|
||||
self.autoSaveProj = 60 # Interval for auto-saving project in seconds
|
||||
self.autoSaveDoc = 30 # Interval for auto-saving document in seconds
|
||||
|
||||
## Text Editor
|
||||
self.textFont = None
|
||||
self.textSize = 12
|
||||
self.textFixedW = True
|
||||
self.textWidth = 600
|
||||
self.textMargin = 40
|
||||
self.tabWidth = 40
|
||||
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.textFont = None # Editor font
|
||||
self.textSize = 12 # Editor font size
|
||||
self.textFixedW = True # Keep editor text fixed width
|
||||
self.textWidth = 600 # Editor text width
|
||||
self.textMargin = 40 # Editor/viewer text margin
|
||||
self.tabWidth = 40 # Editor tabulator width
|
||||
|
||||
self.wordCountTimer = 5.0
|
||||
self.showTabsNSpaces = False
|
||||
self.showLineEndings = False
|
||||
self.bigDocLimit = 800
|
||||
self.showFullPath = True
|
||||
self.highlightQuotes = True
|
||||
self.highlightEmph = True
|
||||
self.focusWidth = 800 # Focus Mode text width
|
||||
self.hideFocusFooter = False # Hide document footer in Focus Mode
|
||||
self.showFullPath = True # Show full document path in editor header
|
||||
self.autoSelect = True # Auto-select word when applying format with no selection
|
||||
|
||||
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.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO]
|
||||
self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO]
|
||||
|
||||
## Spell Checking
|
||||
self.spellTool = None
|
||||
self.spellLanguage = None
|
||||
|
||||
## Search Bar Switches
|
||||
self.searchCase = False
|
||||
self.searchWord = False
|
||||
self.searchRegEx = False
|
||||
@@ -465,11 +473,11 @@ class Config:
|
||||
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.autoScroll = self._parseLine(
|
||||
cnfParse, cnfSec, "autoscroll", self.CNF_BOOL, self.autoScroll
|
||||
)
|
||||
self.scollToPoint = self._parseLine(
|
||||
cnfParse, cnfSec, "scolltopoint", self.CNF_INT, self.scollToPoint
|
||||
self.autoScrollPos = self._parseLine(
|
||||
cnfParse, cnfSec, "autoscrollpos", self.CNF_INT, self.autoScrollPos
|
||||
)
|
||||
self.fmtSingleQuotes = self._parseLine(
|
||||
cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes
|
||||
@@ -616,8 +624,8 @@ class Config:
|
||||
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, "scolltopoint", str(self.scollToPoint))
|
||||
cnfParse.set(cnfSec, "autoscroll", str(self.autoScroll))
|
||||
cnfParse.set(cnfSec, "autoscrollpos", str(self.autoScrollPos))
|
||||
cnfParse.set(cnfSec, "fmtsinglequote", self._packList(self.fmtSingleQuotes))
|
||||
cnfParse.set(cnfSec, "fmtdoublequote", self._packList(self.fmtDoubleQuotes))
|
||||
cnfParse.set(cnfSec, "spelltool", str(self.spellTool))
|
||||
@@ -911,16 +919,21 @@ class Config:
|
||||
"""
|
||||
if cnfParse.has_section(cnfSec):
|
||||
if cnfParse.has_option(cnfSec, cnfName):
|
||||
if cnfType == self.CNF_STR:
|
||||
return cnfParse.get(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_INT:
|
||||
return cnfParse.getint(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_BOOL:
|
||||
return cnfParse.getboolean(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_LIST:
|
||||
return self._unpackList(
|
||||
cnfParse.get(cnfSec, cnfName), len(cnfDefault), cnfDefault
|
||||
)
|
||||
try:
|
||||
if cnfType == self.CNF_STR:
|
||||
return cnfParse.get(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_INT:
|
||||
return cnfParse.getint(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_BOOL:
|
||||
return cnfParse.getboolean(cnfSec, cnfName)
|
||||
elif cnfType == self.CNF_LIST:
|
||||
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
|
||||
|
||||
def _checkNone(self, checkVal):
|
||||
|
||||
+32
-22
@@ -61,6 +61,11 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
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):
|
||||
QTextEdit.__init__(self, theParent)
|
||||
|
||||
@@ -436,8 +441,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
if self.mainConf.scrollPastEnd:
|
||||
docFrame = self.qDocument.rootFrame().frameFormat()
|
||||
docMargin = wH - uM - lM - 4*tB - 5*self.theTheme.fontPixelSize
|
||||
docFrame.setBottomMargin(max(0, docMargin))
|
||||
docFrame.setBottomMargin(max(0, 0.6*(wH - uM - lM - 4*tB)))
|
||||
self.qDocument.rootFrame().setFrameFormat(docFrame)
|
||||
|
||||
return
|
||||
@@ -765,33 +769,39 @@ class GuiDocEditor(QTextEdit):
|
||||
return
|
||||
elif keyEvent == QKeySequence.Redo:
|
||||
self.docAction(nwDocAction.REDO)
|
||||
return
|
||||
elif keyEvent == QKeySequence.Undo:
|
||||
self.docAction(nwDocAction.UNDO)
|
||||
return
|
||||
elif keyEvent == QKeySequence.SelectAll:
|
||||
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:
|
||||
QTextEdit.keyPressEvent(self, keyEvent)
|
||||
self.docFooter.updateLineCount()
|
||||
|
||||
if self.mainConf.scollWithCursor:
|
||||
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()
|
||||
self.docFooter.updateLineCount()
|
||||
|
||||
return
|
||||
|
||||
|
||||
+14
-14
@@ -569,23 +569,23 @@ class GuiConfigEditLayoutTab(QWidget):
|
||||
)
|
||||
|
||||
## Typewriter Scrolling
|
||||
self.scollWithCursor = QSwitch()
|
||||
self.scollWithCursor.setChecked(self.mainConf.scollWithCursor)
|
||||
self.autoScroll = QSwitch()
|
||||
self.autoScroll.setChecked(self.mainConf.autoScroll)
|
||||
self.mainForm.addRow(
|
||||
"Typewriter style scrolling when you type",
|
||||
self.scollWithCursor,
|
||||
self.autoScroll,
|
||||
"Tries to keep the cursor at a fixed vertical position."
|
||||
)
|
||||
|
||||
## Font Size
|
||||
self.scollToPoint = QSpinBox(self)
|
||||
self.scollToPoint.setMinimum(10)
|
||||
self.scollToPoint.setMaximum(90)
|
||||
self.scollToPoint.setSingleStep(1)
|
||||
self.scollToPoint.setValue(self.mainConf.scollToPoint)
|
||||
self.autoScrollPos = QSpinBox(self)
|
||||
self.autoScrollPos.setMinimum(10)
|
||||
self.autoScrollPos.setMaximum(90)
|
||||
self.autoScrollPos.setSingleStep(1)
|
||||
self.autoScrollPos.setValue(int(self.mainConf.autoScrollPos))
|
||||
self.mainForm.addRow(
|
||||
"Position in the editor to keep the cursor",
|
||||
self.scollToPoint,
|
||||
"Minimum position for Typewriter scrolling",
|
||||
self.autoScrollPos,
|
||||
"In units of percentage of the editor height.",
|
||||
theUnit = "%"
|
||||
)
|
||||
@@ -608,8 +608,8 @@ class GuiConfigEditLayoutTab(QWidget):
|
||||
textMargin = self.textMargin.value()
|
||||
tabWidth = self.tabWidth.value()
|
||||
scrollPastEnd = self.scrollPastEnd.isChecked()
|
||||
scollWithCursor = self.scollWithCursor.isChecked()
|
||||
scollToPoint = self.scollToPoint.value()
|
||||
autoScroll = self.autoScroll.isChecked()
|
||||
autoScrollPos = self.autoScrollPos.value()
|
||||
|
||||
self.mainConf.textFont = textFont
|
||||
self.mainConf.textSize = textSize
|
||||
@@ -621,8 +621,8 @@ class GuiConfigEditLayoutTab(QWidget):
|
||||
self.mainConf.textMargin = textMargin
|
||||
self.mainConf.tabWidth = tabWidth
|
||||
self.mainConf.scrollPastEnd = scrollPastEnd
|
||||
self.mainConf.scollWithCursor = scollWithCursor
|
||||
self.mainConf.scollToPoint = scollToPoint
|
||||
self.mainConf.autoScroll = autoScroll
|
||||
self.mainConf.autoScrollPos = autoScrollPos
|
||||
|
||||
self.mainConf.confChanged = True
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ repdquotes = True
|
||||
repdash = True
|
||||
repdots = True
|
||||
scrollpastend = True
|
||||
scollwithcursor = False
|
||||
scolltopoint = 40
|
||||
autoscroll = False
|
||||
autoscrollpos = 30
|
||||
fmtsinglequote = ‘, ’
|
||||
fmtdoublequote = “, ”
|
||||
spelltool = internal
|
||||
|
||||
@@ -40,8 +40,8 @@ repdquotes = True
|
||||
repdash = True
|
||||
repdots = True
|
||||
scrollpastend = False
|
||||
scollwithcursor = True
|
||||
scolltopoint = 40
|
||||
autoscroll = True
|
||||
autoscrollpos = 30
|
||||
fmtsinglequote = ‘, ’
|
||||
fmtdoublequote = “, ”
|
||||
spelltool = internal
|
||||
|
||||
@@ -1127,9 +1127,9 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
|
||||
assert not tabLayout.scrollPastEnd.isChecked()
|
||||
|
||||
qtbot.wait(keyDelay)
|
||||
assert not tabLayout.scollWithCursor.isChecked()
|
||||
qtbot.mouseClick(tabLayout.scollWithCursor, Qt.LeftButton)
|
||||
assert tabLayout.scollWithCursor.isChecked()
|
||||
assert not tabLayout.autoScroll.isChecked()
|
||||
qtbot.mouseClick(tabLayout.autoScroll, Qt.LeftButton)
|
||||
assert tabLayout.autoScroll.isChecked()
|
||||
|
||||
# Editor Settings
|
||||
qtbot.wait(keyDelay)
|
||||
|
||||
+2
-2
@@ -172,8 +172,8 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
|
||||
nwGUI.mainConf.hideHScroll = True
|
||||
nwGUI.mainConf.hideVScroll = True
|
||||
nwGUI.mainConf.scrollPastEnd = True
|
||||
nwGUI.mainConf.scollToPoint = 80
|
||||
nwGUI.mainConf.scollWithCursor = True
|
||||
nwGUI.mainConf.autoScrollPos = 80
|
||||
nwGUI.mainConf.autoScroll = True
|
||||
|
||||
# Add a Character File
|
||||
nwGUI.setFocus(1)
|
||||
|
||||
Reference in New Issue
Block a user