Add Quick Links to the Project Tree (#1165)

This commit is contained in:
Veronica Berglyd Olsen
2022-10-14 17:43:23 +02:00
committed by GitHub
10 changed files with 160 additions and 22 deletions
+1 -1
View File
@@ -71,5 +71,5 @@ On Windows, file extensions may not be visible by default, so make sure you only
extension, and don't end up with two. extension, and don't end up with two.
The QSS files are Qt Style Sheet files. See Qt's The QSS files are Qt Style Sheet files. See Qt's
`The Style Sheet Syntax <https://doc.qt.io/qt-5/stylesheet-syntax.html>`_` documentation for more `The Style Sheet Syntax <https://doc.qt.io/qt-5/stylesheet-syntax.html>`_ documentation for more
details. details.
+1
View File
@@ -47,6 +47,7 @@ The main shorcuts are as follows:
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)" ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)." ":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`." ":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
":kbd:`Ctrl`:kbd:`L`", "Open the :guilabel:`Quick Links` menu in the Project Tree."
":kbd:`Ctrl`:kbd:`N`", "Create new project item." ":kbd:`Ctrl`:kbd:`N`", "Create new project item."
":kbd:`Ctrl`:kbd:`O`", "Open selected document." ":kbd:`Ctrl`:kbd:`O`", "Open selected document."
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter." ":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
@@ -18,6 +18,7 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
[Map] [Map]
add = typ_plus.svg add = typ_plus.svg
backward = typ_chevron-left.svg backward = typ_chevron-left.svg
bookmark = typ_bookmark.svg
bullet-off = typ_media-record-outline.svg bullet-off = typ_media-record-outline.svg
bullet-on = typ_media-record.svg bullet-on = typ_media-record.svg
check = typ_tick.svg check = typ_tick.svg
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.2"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg901">
<metadata
id="metadata907">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs905" />
<path
d="M 16.203226,2 H 7.796774 C 6.05874,2 4.6443545,3.4143855 4.6443545,5.1524195 V 19.86371 c 0,0.540115 0.1092839,0.994063 0.3236484,1.350287 0.5926549,0.982504 1.9072138,1.059213 2.9559187,0.0084 L 11.25708,17.889239 c 0.394052,-0.393002 1.091787,-0.393002 1.485841,0 l 3.333158,3.333158 C 16.592024,21.738349 17.128987,22 17.673304,22 c 0.837492,0 1.682342,-0.660957 1.682342,-2.13629 V 5.1524195 C 19.355646,3.4143855 17.941259,2 16.203226,2 Z M 7.796774,4.101613 h 8.406452 c 0.578994,0 1.050806,0.4718121 1.050806,1.0508065 V 15.560658 l -2.575527,-2.361162 c -1.477434,-1.35449 -3.880628,-1.353439 -5.3580615,0 l -2.574476,2.361162 V 5.1524195 c 0,-0.5789944 0.4718121,-1.0508065 1.0508065,-1.0508065 z m 6.431987,12.301792 C 13.635055,15.809699 12.843798,15.482898 12,15.482898 c -0.843797,0 -1.635054,0.327852 -2.2287605,0.920507 l -3.025272,3.025272 v -2.442075 l 3.2848215,-3.011611 c 1.085483,-0.995114 2.851889,-0.995114 3.937372,0 l 3.285871,3.011611 v 2.442075 z"
id="path899"
style="fill:#6699cc;fill-opacity:1;stroke-width:1.05081" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -18,6 +18,7 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
[Map] [Map]
add = typ_plus.svg add = typ_plus.svg
backward = typ_chevron-left.svg backward = typ_chevron-left.svg
bookmark = typ_bookmark.svg
bullet-off = typ_media-record-outline.svg bullet-off = typ_media-record-outline.svg
bullet-on = typ_media-record.svg bullet-on = typ_media-record.svg
check = typ_tick.svg check = typ_tick.svg
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.2"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg901">
<metadata
id="metadata907">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs905" />
<path
d="M 16.203226,2 H 7.796774 C 6.05874,2 4.6443545,3.4143855 4.6443545,5.1524195 V 19.86371 c 0,0.540115 0.1092839,0.994063 0.3236484,1.350287 0.5926549,0.982504 1.9072138,1.059213 2.9559187,0.0084 L 11.25708,17.889239 c 0.394052,-0.393002 1.091787,-0.393002 1.485841,0 l 3.333158,3.333158 C 16.592024,21.738349 17.128987,22 17.673304,22 c 0.837492,0 1.682342,-0.660957 1.682342,-2.13629 V 5.1524195 C 19.355646,3.4143855 17.941259,2 16.203226,2 Z M 7.796774,4.101613 h 8.406452 c 0.578994,0 1.050806,0.4718121 1.050806,1.0508065 V 15.560658 l -2.575527,-2.361162 c -1.477434,-1.35449 -3.880628,-1.353439 -5.3580615,0 l -2.574476,2.361162 V 5.1524195 c 0,-0.5789944 0.4718121,-1.0508065 1.0508065,-1.0508065 z m 6.431987,12.301792 C 13.635055,15.809699 12.843798,15.482898 12,15.482898 c -0.843797,0 -1.635054,0.327852 -2.2287605,0.920507 l -3.025272,3.025272 v -2.442075 l 3.2848215,-3.011611 c 1.085483,-0.995114 2.851889,-0.995114 3.937372,0 l 3.285871,3.011611 v 2.442075 z"
id="path899"
style="fill:#4271ae;fill-opacity:1;stroke-width:1.05081" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+6 -4
View File
@@ -274,11 +274,13 @@ class NWTree:
return rootClasses return rootClasses
def iterRoots(self, itemClass): def iterRoots(self, itemClass):
"""Iterate over all items of a given class. """Iterate over all root items of a given class in order.
""" """
for tHandle, nwItem in self._treeRoots.items(): for tHandle in self._treeOrder:
if nwItem.itemClass == itemClass: nwItem = self.__getitem__(tHandle)
yield tHandle, nwItem if nwItem is not None and nwItem.isRootType():
if itemClass is None or nwItem.itemClass == itemClass:
yield tHandle, nwItem
return return
def isRoot(self, tHandle): def isRoot(self, tHandle):
+80 -12
View File
@@ -121,18 +121,33 @@ class GuiProjectView(QWidget):
## ##
def initSettings(self): def initSettings(self):
"""Initialise GUI elements that depend on specific settings.
"""
self.projTree.initSettings() self.projTree.initSettings()
return return
def clearProject(self): def clearProject(self):
"""Clear project-related GUI content.
"""
self.projBar.clearContent()
self.projTree.clearTree() self.projTree.clearTree()
return return
def saveProjectTree(self): def openProjectTasks(self):
"""Run open project tasks.
"""
self.projBar.buildQuickLinkMenu()
return
def saveProjectTasks(self):
"""Run save project tasks.
"""
self.projTree.saveTreeOrder() self.projTree.saveTreeOrder()
return return
def populateTree(self): def populateTree(self):
"""Build the tree structure from project data.
"""
self.projTree.buildTree() self.projTree.buildTree()
return return
@@ -159,6 +174,13 @@ class GuiProjectView(QWidget):
self.wordCountsChanged.emit() self.wordCountsChanged.emit()
return return
@pyqtSlot(str)
def updateRootItem(self, tHandle):
"""If any root item changes, rebuild the quick link root menu.
"""
self.projBar.buildQuickLinkMenu()
return
# END Class GuiProjectView # END Class GuiProjectView
@@ -197,6 +219,18 @@ class GuiProjectToolBar(QWidget):
self.viewLabel.setContentsMargins(0, 0, 0, 0) self.viewLabel.setContentsMargins(0, 0, 0, 0)
self.viewLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.viewLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# Quick Links
self.mQuick = QMenu()
self.tbQuick = QToolButton(self)
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
self.tbQuick.setShortcut("Ctrl+L")
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
self.tbQuick.setIconSize(QSize(iPx, iPx))
self.tbQuick.setStyleSheet(buttonStyle)
self.tbQuick.setMenu(self.mQuick)
self.tbQuick.setPopupMode(QToolButton.InstantPopup)
# Move Buttons # Move Buttons
self.tbMoveU = QToolButton(self) self.tbMoveU = QToolButton(self)
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up")) self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
@@ -292,6 +326,7 @@ class GuiProjectToolBar(QWidget):
# Assemble # Assemble
self.outerBox = QHBoxLayout() self.outerBox = QHBoxLayout()
self.outerBox.addWidget(self.viewLabel) self.outerBox.addWidget(self.viewLabel)
self.outerBox.addWidget(self.tbQuick)
self.outerBox.addWidget(self.tbMoveU) self.outerBox.addWidget(self.tbMoveU)
self.outerBox.addWidget(self.tbMoveD) self.outerBox.addWidget(self.tbMoveD)
self.outerBox.addWidget(self.tbAdd) self.outerBox.addWidget(self.tbAdd)
@@ -305,6 +340,32 @@ class GuiProjectToolBar(QWidget):
return return
##
# Methods
##
def clearContent(self):
"""Clear dynamic content on the tool bar.
"""
self.mQuick.clear()
return
def buildQuickLinkMenu(self):
"""Build the quick link menu.
"""
logger.verbose("Rebuilding quick links menu")
self.mQuick.clear()
for n, (tHandle, nwItem) in enumerate(self.theProject.tree.iterRoots(None)):
aRoot = self.mQuick.addAction(nwItem.itemName)
aRoot.setData(tHandle)
aRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]))
aRoot.triggered.connect(
lambda n, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True)
)
return
## ##
# Internal Functions # Internal Functions
## ##
@@ -545,8 +606,7 @@ class GuiProjectTree(QTreeWidget):
self._treeMap[pHandle].setExpanded(True) self._treeMap[pHandle].setExpanded(True)
self._alertTreeChange(tHandle, flush=True) self._alertTreeChange(tHandle, flush=True)
self.clearSelection() self.setCurrentItem(trItem)
trItem.setSelected(True)
return True return True
@@ -585,8 +645,7 @@ class GuiProjectTree(QTreeWidget):
self._recordLastMove(cItem, pItem, tIndex) self._recordLastMove(cItem, pItem, tIndex)
self._alertTreeChange(tHandle, flush=True) self._alertTreeChange(tHandle, flush=True)
self.clearSelection() self.setCurrentItem(trItem)
trItem.setSelected(True)
trItem.setExpanded(isExp) trItem.setExpanded(isExp)
return True return True
@@ -790,6 +849,11 @@ class GuiProjectTree(QTreeWidget):
self._treeMap.pop(tHandle, None) self._treeMap.pop(tHandle, None)
self._alertTreeChange(tHandle, flush=True) self._alertTreeChange(tHandle, flush=True)
# These are not emitted by the alert function because the
# item has already been deleted
self.projView.rootFolderChanged.emit(tHandle)
self.projView.treeItemChanged.emit(tHandle)
else: else:
if askFirst: if askFirst:
msgYes = self.mainGui.askQuestion( msgYes = self.mainGui.askQuestion(
@@ -817,6 +881,10 @@ class GuiProjectTree(QTreeWidget):
self._alertTreeChange(tHandle, flush=flush) self._alertTreeChange(tHandle, flush=flush)
self.projView.wordCountsChanged.emit() self.projView.wordCountsChanged.emit()
# This is not emitted by the alert function because the item
# has already been deleted
self.projView.treeItemChanged.emit(tHandle)
return True return True
def setTreeItemValues(self, tHandle): def setTreeItemValues(self, tHandle):
@@ -945,8 +1013,7 @@ class GuiProjectTree(QTreeWidget):
self._postItemMove(sHandle, wCount) self._postItemMove(sHandle, wCount)
self._alertTreeChange(sHandle, flush=True) self._alertTreeChange(sHandle, flush=True)
self.clearSelection() self.setCurrentItem(movItem)
movItem.setSelected(True)
self._lastMove = {} self._lastMove = {}
return True return True
@@ -968,12 +1035,13 @@ class GuiProjectTree(QTreeWidget):
if tItem is None: if tItem is None:
return False return False
self.clearSelection() self.setFocus()
self._treeMap[tHandle].setSelected(True) if tHandle in self._treeMap:
self.setCurrentItem(self._treeMap[tHandle])
selItems = self.selectedIndexes() selIndex = self.selectedIndexes()
if selItems and doScroll: if selIndex and doScroll:
self.scrollTo(selItems[0], QAbstractItemView.PositionAtCenter) self.scrollTo(selIndex[0], QAbstractItemView.PositionAtCenter)
return True return True
+3 -3
View File
@@ -466,9 +466,9 @@ class GuiIcons:
"view_editor", "view_novel", "view_outline", "view_editor", "view_novel", "view_outline",
# General Button Icons # General Button Icons
"add", "backward", "check", "close", "cross", "down", "edit", "forward", "maximise", "add", "backward", "bookmark", "check", "close", "cross", "down", "edit", "forward",
"menu", "minimise", "reference", "refresh", "remove", "search_replace", "search", "maximise", "menu", "minimise", "reference", "refresh", "remove", "search_replace",
"settings", "up", "search", "settings", "up",
# Switches # Switches
"sticky-on", "sticky-off", "sticky-on", "sticky-off",
+5 -2
View File
@@ -206,6 +206,7 @@ class GuiMain(QMainWindow):
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox) self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem) self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem) self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox) self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
self.novelView.openDocumentRequest.connect(self._openDocument) self.novelView.openDocumentRequest.connect(self._openDocument)
@@ -370,6 +371,7 @@ class GuiMain(QMainWindow):
self.saveProject() self.saveProject()
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.projView.openProjectTasks()
self.novelView.openProjectTasks() self.novelView.openProjectTasks()
self.outlineView.openProjectTasks() self.outlineView.openProjectTasks()
self.rebuildIndex(beQuiet=True) self.rebuildIndex(beQuiet=True)
@@ -522,6 +524,7 @@ class GuiMain(QMainWindow):
self.docEditor.setDictionaries() self.docEditor.setDictionaries()
self.docEditor.toggleSpellCheck(self.theProject.spellCheck) self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
self.statusBar.setRefTime(self.theProject.projOpened) self.statusBar.setRefTime(self.theProject.projOpened)
self.projView.openProjectTasks()
self.novelView.openProjectTasks() self.novelView.openProjectTasks()
self.outlineView.openProjectTasks() self.outlineView.openProjectTasks()
self._updateStatusWordCount() self._updateStatusWordCount()
@@ -556,7 +559,7 @@ class GuiMain(QMainWindow):
logger.error("No project open") logger.error("No project open")
return False return False
self.projView.saveProjectTree() self.projView.saveProjectTasks()
if self.theProject.saveProject(autoSave=autoSave): if self.theProject.saveProject(autoSave=autoSave):
self.theProject.index.saveIndex() self.theProject.index.saveIndex()
@@ -841,7 +844,7 @@ class GuiMain(QMainWindow):
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
tStart = time() tStart = time()
self.projView.saveProjectTree() self.projView.saveProjectTasks()
self.theProject.index.clearIndex() self.theProject.index.clearIndex()
for tItem in self.theProject.tree: for tItem in self.theProject.tree: