Delayed cursor move for large documents speeds up loading significantly
This commit is contained in:
+72
-26
@@ -36,7 +36,7 @@ import logging
|
|||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
Qt, QSize, QThread, QTimer, pyqtSlot, QRegExp, QRegularExpression
|
Qt, QSize, QThread, QTimer, pyqtSlot, QRegExp, QRegularExpression, QPointF
|
||||||
)
|
)
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette,
|
QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette,
|
||||||
@@ -69,21 +69,23 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theTheme = theParent.theTheme
|
self.theTheme = theParent.theTheme
|
||||||
self.theProject = theParent.theProject
|
self.theProject = theParent.theProject
|
||||||
self.docChanged = False
|
|
||||||
self.spellCheck = False
|
|
||||||
self.nwDocument = NWDoc(self.theProject, self.theParent)
|
self.nwDocument = NWDoc(self.theProject, self.theParent)
|
||||||
self.theHandle = None
|
|
||||||
self.theDict = None
|
self.docChanged = False # Flag for changed status of document
|
||||||
|
self.spellCheck = False # Flag for spell checking enabled
|
||||||
|
self.theHandle = None # The handle of the open file
|
||||||
|
self.theDict = None # The current spell check dictionary
|
||||||
|
self.nonWord = "\"'" # Characters to not include in spell checking
|
||||||
|
|
||||||
# Document Variables
|
# Document Variables
|
||||||
self.charCount = 0
|
self.charCount = 0 # Character count
|
||||||
self.wordCount = 0
|
self.wordCount = 0 # Word count
|
||||||
self.paraCount = 0
|
self.paraCount = 0 # Paragraph count
|
||||||
self.lastEdit = 0
|
self.lastEdit = 0 # Time stamp of last edit
|
||||||
self.lastFind = None
|
self.lastFind = None # Position of the last found search word
|
||||||
self.bigDoc = False
|
self.bigDoc = False # Flag for very large document size
|
||||||
self.doReplace = False
|
self.doReplace = False # Switch to temporarily disable auto-replace
|
||||||
self.nonWord = "\"'"
|
self.queuePos = None # Used for delayed change of cursor position
|
||||||
|
|
||||||
# Typography
|
# Typography
|
||||||
self.typDQOpen = self.mainConf.fmtDoubleQuotes[0]
|
self.typDQOpen = self.mainConf.fmtDoubleQuotes[0]
|
||||||
@@ -94,6 +96,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
# Core Elements and Signals
|
# Core Elements and Signals
|
||||||
self.qDocument = self.document()
|
self.qDocument = self.document()
|
||||||
self.qDocument.contentsChange.connect(self._docChange)
|
self.qDocument.contentsChange.connect(self._docChange)
|
||||||
|
self.qDocument.documentLayout().documentSizeChanged.connect(self._docSizeChanged)
|
||||||
|
|
||||||
# Document Title
|
# Document Title
|
||||||
self.docHeader = GuiDocEditHeader(self)
|
self.docHeader = GuiDocEditHeader(self)
|
||||||
@@ -161,8 +164,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.wordCount = 0
|
self.wordCount = 0
|
||||||
self.paraCount = 0
|
self.paraCount = 0
|
||||||
self.lastEdit = 0
|
self.lastEdit = 0
|
||||||
|
self.lastFind = None
|
||||||
self.bigDoc = False
|
self.bigDoc = False
|
||||||
self.doReplace = False
|
self.doReplace = False
|
||||||
|
self.queuePos = None
|
||||||
|
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
self.docHeader.setTitleFromHandle(self.theHandle)
|
self.docHeader.setTitleFromHandle(self.theHandle)
|
||||||
@@ -216,6 +221,12 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
self.qDocument.setDefaultTextOption(theOpt)
|
self.qDocument.setDefaultTextOption(theOpt)
|
||||||
|
|
||||||
|
# Refresh the tab stops
|
||||||
|
if self.mainConf.verQtValue >= 51000:
|
||||||
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
|
else:
|
||||||
|
self.setTabStopWidth(self.mainConf.getTabWidth())
|
||||||
|
|
||||||
# Initialise the syntax highlighter
|
# Initialise the syntax highlighter
|
||||||
self.hLight.initHighlighter()
|
self.hLight.initHighlighter()
|
||||||
|
|
||||||
@@ -249,7 +260,8 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
# Check that the document is not too big for full, initial spell
|
# Check that the document is not too big for full, initial spell
|
||||||
# checking. If it is too big, we switch to only check as we type
|
# checking. If it is too big, we switch to only check as we type
|
||||||
self._checkDocSize(len(theDoc))
|
docSize = len(theDoc)
|
||||||
|
self._checkDocSize(docSize)
|
||||||
spTemp = self.hLight.spellCheck
|
spTemp = self.hLight.spellCheck
|
||||||
if self.bigDoc:
|
if self.bigDoc:
|
||||||
self.hLight.spellCheck = False
|
self.hLight.spellCheck = False
|
||||||
@@ -257,16 +269,12 @@ class GuiDocEditor(QTextEdit):
|
|||||||
bfTime = time()
|
bfTime = time()
|
||||||
self._allowAutoReplace(False)
|
self._allowAutoReplace(False)
|
||||||
self.setPlainText(theDoc)
|
self.setPlainText(theDoc)
|
||||||
|
qApp.processEvents()
|
||||||
|
|
||||||
self._allowAutoReplace(True)
|
self._allowAutoReplace(True)
|
||||||
afTime = time()
|
afTime = time()
|
||||||
logger.debug("Document highlighted in %.3f milliseconds" % (1000*(afTime-bfTime)))
|
logger.debug("Document highlighted in %.3f milliseconds" % (1000*(afTime-bfTime)))
|
||||||
|
|
||||||
theItem = self.nwDocument.getCurrentItem()
|
|
||||||
if tLine is None and theItem is not None:
|
|
||||||
self.setCursorPosition(theItem.cursorPos)
|
|
||||||
else:
|
|
||||||
self.setCursorLine(tLine)
|
|
||||||
|
|
||||||
self.lastEdit = time()
|
self.lastEdit = time()
|
||||||
self._runCounter()
|
self._runCounter()
|
||||||
self.wcTimer.start()
|
self.wcTimer.start()
|
||||||
@@ -280,11 +288,18 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.hLight.spellCheck = spTemp
|
self.hLight.spellCheck = spTemp
|
||||||
qApp.restoreOverrideCursor()
|
qApp.restoreOverrideCursor()
|
||||||
|
|
||||||
# Refresh the tab stops
|
theItem = self.nwDocument.getCurrentItem()
|
||||||
if self.mainConf.verQtValue >= 51000:
|
if tLine is None and theItem is not None:
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
# For large documents we queue the repositioning until the
|
||||||
|
# document layout has grown past the point we want to move
|
||||||
|
# the cursor to. This makes the loading significantly
|
||||||
|
# faster.
|
||||||
|
if docSize > 50000:
|
||||||
|
self.queuePos = theItem.cursorPos
|
||||||
|
else:
|
||||||
|
self.setCursorPosition(theItem.cursorPos)
|
||||||
else:
|
else:
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
self.setCursorLine(tLine)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -318,11 +333,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
docText = self.getText()
|
docText = self.getText()
|
||||||
cursPos = self.getCursorPosition()
|
|
||||||
theItem.setCharCount(self.charCount)
|
theItem.setCharCount(self.charCount)
|
||||||
theItem.setWordCount(self.wordCount)
|
theItem.setWordCount(self.wordCount)
|
||||||
theItem.setParaCount(self.paraCount)
|
theItem.setParaCount(self.paraCount)
|
||||||
theItem.setCursorPos(cursPos)
|
self.saveCursorPosition()
|
||||||
self.nwDocument.saveDocument(docText)
|
self.nwDocument.saveDocument(docText)
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
|
|
||||||
@@ -431,6 +445,15 @@ class GuiDocEditor(QTextEdit):
|
|||||||
"""
|
"""
|
||||||
return self.textCursor().selectionEnd()
|
return self.textCursor().selectionEnd()
|
||||||
|
|
||||||
|
def saveCursorPosition(self):
|
||||||
|
"""Save the cursor position to the current project otem.
|
||||||
|
"""
|
||||||
|
theItem = self.nwDocument.getCurrentItem()
|
||||||
|
if theItem is not None:
|
||||||
|
cursPos = self.getCursorPosition()
|
||||||
|
theItem.setCursorPos(cursPos)
|
||||||
|
return
|
||||||
|
|
||||||
def setCursorLine(self, theLine):
|
def setCursorLine(self, theLine):
|
||||||
"""Move the cursor to a given line in the document.
|
"""Move the cursor to a given line in the document.
|
||||||
"""
|
"""
|
||||||
@@ -898,6 +921,29 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot("QSizeF")
|
||||||
|
def _docSizeChanged(self, theSize):
|
||||||
|
"""Called whenever the underlying document layout size changes.
|
||||||
|
This is used to queue the repositioning of the cursor for very
|
||||||
|
large documents to ensure the region where the cursor is being
|
||||||
|
moved to has been drawn before the move is made.
|
||||||
|
"""
|
||||||
|
if self.queuePos is not None:
|
||||||
|
thePos = self.qDocument.documentLayout().hitTest(
|
||||||
|
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
|
||||||
|
)
|
||||||
|
if self.queuePos <= thePos:
|
||||||
|
logger.verbose(
|
||||||
|
"Allowed cursor move to %d <= %d" % (self.queuePos, thePos)
|
||||||
|
)
|
||||||
|
self.setCursorPosition(self.queuePos)
|
||||||
|
self.queuePos = None
|
||||||
|
else:
|
||||||
|
logger.verbose(
|
||||||
|
"Denied cursor move to %d > %d" % (self.queuePos, thePos)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -448,6 +448,7 @@ class GuiMain(QMainWindow):
|
|||||||
"""Close the document and clear the editor and title field.
|
"""Close the document and clear the editor and title field.
|
||||||
"""
|
"""
|
||||||
if self.hasProject:
|
if self.hasProject:
|
||||||
|
self.docEditor.saveCursorPosition()
|
||||||
if self.docEditor.docChanged:
|
if self.docEditor.docChanged:
|
||||||
self.saveDocument()
|
self.saveDocument()
|
||||||
self.docEditor.clearEditor()
|
self.docEditor.clearEditor()
|
||||||
|
|||||||
Reference in New Issue
Block a user