Merge pull request #779 from vkbo/editor_cleanup

Editor Code Improvements
This commit is contained in:
Veronica Berglyd Olsen
2021-05-16 19:02:57 +02:00
committed by GitHub
13 changed files with 415 additions and 318 deletions
+1 -1
View File
@@ -1109,7 +1109,7 @@ class NWProject():
information to the GUI statusbar.
"""
self.projChanged = bValue
self.theParent.setProjectStatus(self.projChanged)
self.theParent.statusBar.doUpdateProjectStatus(bValue)
if bValue:
# If we've changed the project at all, this should be True
self.projAltered = True
+3 -3
View File
@@ -152,7 +152,7 @@ class NWSpellEnchant(NWSpellCheck):
except Exception:
logger.error("Failed to load enchant spell checking for language %s" % theLang)
self.theDict = NWSpellEnchantDummy()
self.theDict = FakeEnchant()
self.spellLanguage = None
self._readProjectDictionary(projectDict)
@@ -208,7 +208,7 @@ class NWSpellEnchant(NWSpellCheck):
# END Class NWSpellEnchant
class NWSpellEnchantDummy:
class FakeEnchant:
"""Fallback for when Enchant is selected, but not installed.
"""
def __init__(self):
@@ -223,7 +223,7 @@ class NWSpellEnchantDummy:
def add_to_session(self, theWord):
return
# END Class NWSpellEnchantDummy
# END Class FakeEnchant
# =============================================================================================== #
# Fallback SpellChecking Using difflib
+1 -1
View File
@@ -207,7 +207,7 @@ class GuiProjectEditMain(QWidget):
self.spellLang = QComboBox(self)
self.spellLang.setMaximumWidth(xW)
theDict = self.theParent.docEditor.theDict
theDict = self.theParent.docEditor.currentDictionary()
self.spellLang.addItem(self.tr("Default"), "None")
if theDict is not None:
for spTag, spProv in theDict.listDictionaries():
+8
View File
@@ -112,6 +112,14 @@ class nwAlert(Enum):
# END Enum nwAlert
class nwState(Enum):
NONE = 0
BAD = 1
GOOD = 2
# END Enum nwState
class nwWidget(Enum):
TREE = 1
+263 -229
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -876,7 +876,7 @@ class GuiDocViewHeader(QWidget):
def _refreshDocument(self):
"""Reload the content of the document.
"""
if self.docViewer.theHandle == self.theParent.docEditor.theHandle:
if self.docViewer.theHandle == self.theParent.docEditor.docHandle():
self.theParent.saveDocument()
self.docViewer.reloadText()
return
+17 -12
View File
@@ -27,7 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import nw
import logging
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
@@ -212,17 +212,6 @@ class GuiItemDetails(QWidget):
return
def updateCounts(self, tHandle, cC, wC, pC):
"""Update the counts if the handle is the same as the one we're
already showing. Otherwise, do nothing.
"""
if tHandle == self.theHandle:
self.cCountData.setText(f"{cC:n}")
self.wCountData.setText(f"{wC:n}")
self.pCountData.setText(f"{pC:n}")
return
def updateViewBox(self, tHandle):
"""Populate the details box from a given handle.
"""
@@ -281,4 +270,20 @@ class GuiItemDetails(QWidget):
return
##
# Slots
##
@pyqtSlot(str, int, int, int)
def doUpdateCounts(self, tHandle, cC, wC, pC):
"""Update the counts if the handle is the same as the one we're
already showing. Otherwise, do nothing.
"""
if tHandle == self.theHandle:
self.cCountData.setText(f"{cC:n}")
self.wCountData.setText(f"{wC:n}")
self.pCountData.setText(f"{pC:n}")
return
# END Class GuiItemDetails
+24 -3
View File
@@ -30,7 +30,7 @@ import logging
from time import time
from PyQt5.QtCore import Qt, QSize, pyqtSignal
from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction
@@ -51,6 +51,7 @@ class GuiProjectTree(QTreeWidget):
novelItemChanged = pyqtSignal()
noteItemChanged = pyqtSignal()
projectWordCountChanged = pyqtSignal(int, int)
def __init__(self, theParent):
QTreeWidget.__init__(self, theParent)
@@ -524,7 +525,7 @@ class GuiProjectTree(QTreeWidget):
tIndex = trItemP.indexOfChild(trItemS)
trItemC = trItemP.takeChild(tIndex)
if self.theParent.docEditor.theHandle == tHandle:
if self.theParent.docEditor.docHandle() == tHandle:
self.theParent.closeDocument()
delDoc = NWDoc(self.theProject, tHandle)
@@ -674,7 +675,8 @@ class GuiProjectTree(QTreeWidget):
self.theProject.setProjectWordCount(nWords)
sWords = self.theProject.getSessionWordCount()
self.theParent.statusBar.setStats(nWords, sWords)
self.projectWordCountChanged.emit(nWords, sWords)
return
@@ -779,6 +781,7 @@ class GuiProjectTree(QTreeWidget):
# Slots
##
@pyqtSlot("QPoint")
def _rightClickMenu(self, clickPos):
"""The user right clicked an element in the project tree, so we
open a context menu in-place.
@@ -795,6 +798,14 @@ class GuiProjectTree(QTreeWidget):
return
@pyqtSlot(str, int, int, int)
def doUpdateCounts(self, tHandle, cCount, wCount, pCount):
"""Slot for updating the word count of a specific item.
"""
self.propagateCount(tHandle, wCount)
self.projectWordCount()
return
##
# Events
##
@@ -1171,6 +1182,7 @@ class GuiProjectTreeMenu(QMenu):
# Slots
##
@pyqtSlot()
def _doOpenItem(self):
"""Forward the open document call to the main GUI window.
"""
@@ -1178,6 +1190,7 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.theParent.openDocument(self.theItem.itemHandle, doScroll=False)
return
@pyqtSlot()
def _doViewItem(self):
"""Forward the view document call to the main GUI window.
"""
@@ -1185,6 +1198,7 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.theParent.viewDocument(self.theItem.itemHandle)
return
@pyqtSlot()
def _doEditItem(self):
"""Forward the edit item call to the main GUI window.
"""
@@ -1192,6 +1206,7 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.theParent.editItem()
return
@pyqtSlot()
def _doMakeFile(self):
"""Forward the new file call to the project tree.
"""
@@ -1199,6 +1214,7 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.newTreeItem(nwItemType.FILE, None)
return
@pyqtSlot()
def _doMakeFolder(self):
"""Forward the new folder call to the project tree.
"""
@@ -1206,6 +1222,7 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.newTreeItem(nwItemType.FOLDER, None)
return
@pyqtSlot()
def _doToggleExported(self):
"""Flip the isExported flag of the current item.
"""
@@ -1214,6 +1231,7 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.setTreeItemValues(self.theItem.itemHandle)
return
@pyqtSlot()
def _doDeleteItem(self):
"""Forward the delete item call to the project tree.
"""
@@ -1221,18 +1239,21 @@ class GuiProjectTreeMenu(QMenu):
self.theTree.deleteItem()
return
@pyqtSlot()
def _doEmptyTrash(self):
"""Forward the empty trash call to the project tree.
"""
self.theTree.emptyTrash()
return
@pyqtSlot()
def _doMoveUp(self):
"""Forward the move item call to the project tree.
"""
self.theTree.moveTreeItem(-1)
return
@pyqtSlot()
def _doMoveDown(self):
"""Forward the move item call to the project tree.
"""
+65 -43
View File
@@ -29,11 +29,12 @@ import logging
from time import time
from PyQt5.QtCore import QLocale
from PyQt5.QtCore import QLocale, pyqtSlot
from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
from nw.common import formatTime
from nw.enum import nwState
logger = logging.getLogger(__name__)
@@ -123,10 +124,10 @@ class GuiMainStatus(QStatusBar):
"""Reset all widgets on the status bar to default values.
"""
self.setRefTime(None)
self.setLanguage(None)
self.setStats(0, 0)
self.setProjectStatus(None)
self.setDocumentStatus(None)
self.setLanguage(None, "")
self.doUpdateProjectStats(0, 0)
self.setProjectStatus(nwState.NONE)
self.setDocumentStatus(nwState.NONE)
self.updateTime()
return True
@@ -147,40 +148,16 @@ class GuiMainStatus(QStatusBar):
qApp.processEvents()
return
def setLanguage(self, theLanguage, theProvider=""):
"""Set the language code for the spell checker.
"""
if theLanguage is None:
self.langText.setText(self.tr("None"))
self.langText.setToolTip("")
else:
qLocal = QLocale(theLanguage)
spLang = qLocal.nativeLanguageName().title()
self.langText.setText(spLang)
if theProvider:
self.langText.setToolTip("%s (%s)" % (theLanguage, theProvider))
else:
self.langText.setToolTip(theLanguage)
return
def setProjectStatus(self, isChanged):
def setProjectStatus(self, theState):
"""Set the project status colour icon.
"""
self.projIcon.setState(isChanged)
self.projIcon.setState(theState)
return
def setDocumentStatus(self, isChanged):
def setDocumentStatus(self, theState):
"""Set the document status colour icon.
"""
self.docIcon.setState(isChanged)
return
def setStats(self, pWC, sWC):
"""Set the current project statistics.
"""
self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
self.statsText.setToolTip(self.tr("Project word count (session change)"))
self.docIcon.setState(theState)
return
def setUserIdle(self, userIdle):
@@ -212,16 +189,60 @@ class GuiMainStatus(QStatusBar):
self.timeText.setText(formatTime(sessTime))
return
##
# Slots
##
@pyqtSlot(str, str)
def setLanguage(self, theLanguage, theProvider):
"""Set the language code for the spell checker.
"""
if theLanguage == "None":
self.langText.setText(self.tr("None"))
self.langText.setToolTip("")
else:
qLocal = QLocale(theLanguage)
spLang = qLocal.nativeLanguageName().title()
self.langText.setText(spLang)
if theProvider:
self.langText.setToolTip("%s (%s)" % (theLanguage, theProvider))
else:
self.langText.setToolTip(theLanguage)
return
@pyqtSlot(int, int)
def doUpdateProjectStats(self, pWC, sWC):
"""Update the current project statistics.
"""
self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
self.statsText.setToolTip(self.tr("Project word count (session change)"))
return
@pyqtSlot(bool)
def doUpdateProjectStatus(self, isChanged):
"""Slot for updating the project status.
"""
self.setProjectStatus(nwState.GOOD if isChanged else nwState.BAD)
return
@pyqtSlot(bool)
def doUpdateDocumentStatus(self, isChanged):
"""Slot for updating the document status.
"""
self.setDocumentStatus(nwState.GOOD if isChanged else nwState.BAD)
return
# END Class GuiMainStatus
class StatusLED(QAbstractButton):
def __init__(self, colNone, colTrue, colFalse, sW, sH, parent=None):
def __init__(self, colNone, colGood, colBad, sW, sH, parent=None):
super().__init__(parent=parent)
self.colNone = colNone
self.colTrue = colTrue
self.colFalse = colFalse
self._colNone = colNone
self._colGood = colGood
self._colBad = colBad
self._theCol = colNone
self.setFixedWidth(sW)
@@ -236,11 +257,12 @@ class StatusLED(QAbstractButton):
def setState(self, theState):
"""Set the colour state.
"""
self._theCol = self.colNone
if theState is True:
self._theCol = self.colTrue
elif theState is False:
self._theCol = self.colFalse
if theState == nwState.GOOD:
self._theCol = self._colGood
elif theState == nwState.BAD:
self._theCol = self._colBad
else:
self._theCol = self._colNone
self.update()
@@ -250,7 +272,7 @@ class StatusLED(QAbstractButton):
# Events
##
def paintEvent(self, event):
def paintEvent(self, _):
"""Drawing the LED.
"""
qPalette = self.palette()
+24 -17
View File
@@ -49,7 +49,7 @@ from nw.dialogs import (
)
from nw.tools import GuiBuildNovel, GuiProjectWizard, GuiWritingStats
from nw.core import NWProject, NWIndex
from nw.enum import nwItemType, nwItemClass, nwAlert, nwWidget
from nw.enum import nwItemType, nwItemClass, nwAlert, nwWidget, nwState
from nw.common import getGuiItem, hexToInt
from nw.constants import nwLists
@@ -119,6 +119,17 @@ class GuiMain(QMainWindow):
self.projMeta = GuiOutlineDetails(self)
self.mainMenu = GuiMainMenu(self)
# Connect Signals Between Main Elements
self.docEditor.spellDictionaryChanged.connect(self.statusBar.setLanguage)
self.docEditor.docEditedStatusChanged.connect(self.statusBar.doUpdateDocumentStatus)
self.docEditor.docCountsChanged.connect(self.treeMeta.doUpdateCounts)
self.docEditor.docCountsChanged.connect(self.treeView.doUpdateCounts)
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
self.treeView.novelItemChanged.connect(self._treeNovelItemChanged)
self.treeView.projectWordCountChanged.connect(self.statusBar.doUpdateProjectStats)
# Minor GUI Elements
self.statusIcons = []
self.importIcons = []
@@ -227,9 +238,6 @@ class GuiMain(QMainWindow):
self.docEditor.closeSearch()
# Initialise the Project Tree
self.treeView.itemSelectionChanged.connect(self._treeSingleClick)
self.treeView.itemDoubleClicked.connect(self._treeDoubleClick)
self.treeView.novelItemChanged.connect(self._treeNovelItemChanged)
self.rebuildTrees()
# Set Main Window Elements
@@ -267,7 +275,6 @@ class GuiMain(QMainWindow):
# Forward Functions
self.setStatus = self.statusBar.setStatus
self.setProjectStatus = self.statusBar.setProjectStatus
# Force a show of the GUI
self.show()
@@ -377,8 +384,8 @@ class GuiMain(QMainWindow):
self.docEditor.setDictionaries()
self.rebuildIndex(beQuiet=True)
self.statusBar.setRefTime(self.theProject.projOpened)
self.statusBar.setProjectStatus(True)
self.statusBar.setDocumentStatus(None)
self.statusBar.setProjectStatus(nwState.GOOD)
self.statusBar.setDocumentStatus(nwState.NONE)
self.statusBar.setStatus(self.tr("New project created ..."))
self._updateWindowTitle(self.theProject.projName)
else:
@@ -407,7 +414,7 @@ class GuiMain(QMainWindow):
if not msgYes:
return False
if self.docEditor.docChanged:
if self.docEditor.docChanged():
self.saveDocument()
if self.theProject.projAltered:
@@ -520,7 +527,7 @@ class GuiMain(QMainWindow):
self.docEditor.setSpellCheck(self.theProject.spellCheck)
self.mainMenu.setAutoOutline(self.theProject.autoOutline)
self.statusBar.setRefTime(self.theProject.projOpened)
self.statusBar.setStats(self.theProject.currWCount, 0)
self.statusBar.doUpdateProjectStats(self.theProject.currWCount, 0)
# Restore previously open documents, if any
if self.theProject.lastEdited is not None:
@@ -576,7 +583,7 @@ class GuiMain(QMainWindow):
self.toggleFocusMode()
self.docEditor.saveCursorPosition()
if self.docEditor.docChanged:
if self.docEditor.docChanged():
self.saveDocument()
self.docEditor.clearEditor()
@@ -658,7 +665,7 @@ class GuiMain(QMainWindow):
if self.docEditor.hasFocus():
logger.verbose("Trying editor document")
tHandle = self.docEditor.theHandle
tHandle = self.docEditor.docHandle()
if tHandle is not None:
self.saveDocument()
@@ -727,7 +734,7 @@ class GuiMain(QMainWindow):
], nwAlert.ERROR)
return False
if self.docEditor.theHandle is None:
if self.docEditor.docHandle() is None:
self.makeAlert(
self.tr("Please open a document to import the text file into."),
nwAlert.ERROR
@@ -822,7 +829,7 @@ class GuiMain(QMainWindow):
if tHandle is None:
if self.docEditor.anyFocus() or self.isFocusMode:
tHandle = self.docEditor.theHandle
tHandle = self.docEditor.docHandle()
else:
tHandle = self.treeView.getSelectedHandle()
@@ -878,7 +885,7 @@ class GuiMain(QMainWindow):
self.treeView.saveTreeOrder()
self.theIndex.clearIndex()
for nDone, tItem in enumerate(self.theProject.projTree):
for tItem in self.theProject.projTree:
if tItem is not None:
self.setStatus(self.tr("Indexing: '{0}'").format(tItem.itemName))
@@ -1218,7 +1225,7 @@ class GuiMain(QMainWindow):
"""Main GUI Focus Mode hides tree, view pane and optionally also
statusbar and menu.
"""
if self.docEditor.theHandle is None:
if self.docEditor.docHandle() is None:
logger.error("No document open, so not activating Focus Mode")
self.mainMenu.setFocusMode(self.isFocusMode)
return False
@@ -1385,7 +1392,7 @@ class GuiMain(QMainWindow):
"""Triggered by the auto-save document timer to save the
document.
"""
if self.hasProject and self.docEditor.docChanged:
if self.hasProject and self.docEditor.docChanged():
logger.debug("Autosaving document")
self.saveDocument()
return
@@ -1477,7 +1484,7 @@ class GuiMain(QMainWindow):
return
currTime = time()
editIdle = currTime - self.docEditor.lastActive > self.mainConf.userIdleTime
editIdle = currTime - self.docEditor.lastActive() > self.mainConf.userIdleTime
userIdle = qApp.applicationState() != Qt.ApplicationActive
if editIdle or userIdle:
+3 -3
View File
@@ -54,9 +54,6 @@ class MockGuiMain():
def setStatus(self, theMessage):
return
def setProjectStatus(self, isChanged):
return
def openProject(self, projPath):
return
@@ -89,6 +86,9 @@ class MockStatusBar():
def setStatus(self, theText):
return
def doUpdateProjectStatus(self, theStatus):
return
# END Class MockStatusBar
class MockApp:
+3 -3
View File
@@ -292,9 +292,9 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
qtbot.wait(stepDelay)
# Save the document
assert nwGUI.docEditor.docChanged
assert nwGUI.docEditor.docChanged()
assert nwGUI.saveDocument()
assert not nwGUI.docEditor.docChanged
assert not nwGUI.docEditor.docChanged()
qtbot.wait(stepDelay)
nwGUI.rebuildIndex()
qtbot.wait(stepDelay)
@@ -503,7 +503,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Next Match
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
assert nwGUI.docEditor.theHandle == "2426c6f0ca922" # Next document
assert nwGUI.docEditor.docHandle() == "2426c6f0ca922" # Next document
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
+2 -2
View File
@@ -97,9 +97,9 @@ def testGuiNovelTree_TreeItems(qtbot, caplog, monkeypatch, nwGUI, nwMinimal):
# Double-click item
scItem.setSelected(True)
assert scItem.isSelected()
assert nwGUI.docEditor.theHandle is None
assert nwGUI.docEditor.docHandle() is None
nwTree._treeDoubleClick(scItem, 0)
assert nwGUI.docEditor.theHandle == "8c659a11cd429"
assert nwGUI.docEditor.docHandle() == "8c659a11cd429"
# Open item with middle mouse button
scItem.setSelected(True)