Merge pull request #482 from vkbo/typewriter_mode
Updated Typewriter Mode
This commit is contained in:
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user