From 6ebc4ca268032c3863c06162d121970d081c5be5 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 18:19:22 +0200 Subject: [PATCH 1/9] Cleaned up the way navigation is done in the document viewer --- nw/core/tohtml.py | 2 +- nw/gui/docviewer.py | 24 ++++++++++++++---------- nw/guimain.py | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index cb8cfa1a..720ac9cd 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -189,7 +189,7 @@ class ToHtml(Tokenizer): hStyle = "" if self.linkHeaders: - aNm = "" % (self.theHandle, tLine) + aNm = "" % tLine else: aNm = "" diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 0c8c51b1..17b668c8 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -168,6 +168,7 @@ class GuiDocViewer(QTextBrowser): self.setTabStopWidth(self.mainConf.getTabWidth()) self.setHtml(aDoc.theResult.replace("\t", "!!tab!!")) + self.setDocumentTitle(tHandle) # Loop through the text and put back in the tabs. Tabs are removed by # the setHtml function, so the ToHtml class puts in a placeholder. @@ -203,7 +204,7 @@ class GuiDocViewer(QTextBrowser): index being up to date. """ logger.debug("Loading document from tag '%s'" % theTag) - tHandle, onLine, sTitle = self.theParent.theIndex.getTagSource(theTag) + tHandle, _, sTitle = self.theParent.theIndex.getTagSource(theTag) if tHandle is None: self.theParent.makeAlert(( "Could not find the reference for tag '%s'. It either doesn't " @@ -215,8 +216,9 @@ class GuiDocViewer(QTextBrowser): # Let the parent handle the opening as it also ensures that # the doc view panel is visible in case this request comes # from outside this class. + logger.verbose("Tag points to %s#%s" % (tHandle, sTitle)) self.theParent.viewDocument(tHandle) - self.navigateTo("#head_%s:%s" % (tHandle, sTitle)) + self.navigateTo("#%s" % sTitle) return True def docAction(self, theAction): @@ -240,13 +242,14 @@ class GuiDocViewer(QTextBrowser): return False return True - def navigateTo(self, navLink): + def navigateTo(self, tAnchor): """Go to a specific #link in the document. """ - if not isinstance(navLink, str): + if not isinstance(tAnchor, str): return False - if navLink.startswith("#"): - self.setSource(QUrl(navLink)) + if tAnchor.startswith("#"): + logger.verbose("Moving to anchor %s" % tAnchor) + self.setSource(QUrl(tAnchor)) return True def updateDocMargins(self): @@ -818,7 +821,7 @@ class GuiDocViewDetails(QScrollArea): for tHandle in theRefs: tItem = self.theProject.projTree[tHandle] if tItem is not None: - theList.append("%s" % ( + theList.append("%s" % ( tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName )) @@ -835,9 +838,10 @@ class GuiDocViewDetails(QScrollArea): class for handling. """ logger.verbose("Clicked link: '%s'" % theLink) - if len(theLink) == 27: - tHandle = theLink[6:19] - self.theParent.viewDocument(tHandle, theLink) + if len(theLink) == 21: + tHandle = theLink[:13] + tAnchor = theLink[13:] + self.theParent.viewDocument(tHandle, tAnchor) return # END Class GuiDocViewDetails diff --git a/nw/guimain.py b/nw/guimain.py index 86401a5b..0729a7df 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -525,7 +525,7 @@ class GuiMain(QMainWindow): self.docEditor.saveText() return True - def viewDocument(self, tHandle=None, navLink=None): + def viewDocument(self, tHandle=None, tAnchor=None): """Load a document for viewing in the view panel. """ if tHandle is None: @@ -562,7 +562,7 @@ class GuiMain(QMainWindow): vPos[1] = bPos[1] - vPos[0] self.splitDocs.setSizes(vPos) self.viewMeta.setVisible(self.mainConf.showRefPanel) - self.docViewer.navigateTo(navLink) + self.docViewer.navigateTo(tAnchor) return True From 2a3df92b202c07ebf6bef41ab3fc79505c49dc0b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 18:44:08 +0200 Subject: [PATCH 2/9] Added starte view history class and connected mouse, menu and keyboard --- nw/gui/docviewer.py | 73 +++++++++++++++++++++++++++++++++++++++++---- nw/gui/mainmenu.py | 17 +++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 17b668c8..e8333a00 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -63,20 +63,22 @@ class GuiDocViewer(QTextBrowser): self.setMinimumWidth(self.mainConf.pxInt(300)) self.setAutoFillBackground(True) self.setOpenExternalLinks(False) + self.setFocusPolicy(Qt.StrongFocus) self.initViewer() # Document Header and Footer - self.docHeader = GuiDocViewHeader(self) - self.docFooter = GuiDocViewFooter(self) - self.stickyRef = False + self.docHeader = GuiDocViewHeader(self) + self.docFooter = GuiDocViewFooter(self) + self.docHistory = GuiDocViewHistory(self) + self.stickyRef = False theOpt = QTextOption() if self.mainConf.doJustify: theOpt.setAlignment(Qt.AlignJustify) self.qDocument.setDefaultTextOption(theOpt) + # Signals self.anchorClicked.connect(self._linkClicked) - self.setFocusPolicy(Qt.StrongFocus) # Context Menu self.setContextMenuPolicy(Qt.CustomContextMenu) @@ -169,6 +171,7 @@ class GuiDocViewer(QTextBrowser): self.setHtml(aDoc.theResult.replace("\t", "!!tab!!")) self.setDocumentTitle(tHandle) + self.docHistory.append(tHandle) # Loop through the text and put back in the tabs. Tabs are removed by # the setHtml function, so the ToHtml class puts in a placeholder. @@ -217,8 +220,7 @@ class GuiDocViewer(QTextBrowser): # the doc view panel is visible in case this request comes # from outside this class. logger.verbose("Tag points to %s#%s" % (tHandle, sTitle)) - self.theParent.viewDocument(tHandle) - self.navigateTo("#%s" % sTitle) + self.theParent.viewDocument(tHandle, "#%s" % sTitle) return True def docAction(self, theAction): @@ -252,6 +254,18 @@ class GuiDocViewer(QTextBrowser): self.setSource(QUrl(tAnchor)) return True + def navBackward(self): + """Navigate backwards in the document view history. + """ + self.docHistory.backward() + return + + def navForward(self): + """Navigate forwards in the document view history. + """ + self.docHistory.forward() + return + def updateDocMargins(self): """Automatically adjust the margins so the text is centred if Config.textFixedW is enabled or we're in Focus Mode. Otherwise, @@ -382,6 +396,15 @@ class GuiDocViewer(QTextBrowser): self.updateDocMargins() return + def mouseReleaseEvent(self, theEvent): + """Capture mouse click events on the document. + """ + if theEvent.button() == Qt.BackButton: + self.navBackward() + elif theEvent.button() == Qt.ForwardButton: + self.navForward() + return + ## # Internal Functions ## @@ -481,6 +504,44 @@ class GuiDocViewer(QTextBrowser): # END Class GuiDocViewer +class GuiDocViewHistory(): + + def __init__(self, docViewer): + + self.docViewer = docViewer + + self._navHistory = [] + self._navPosition = -1 + + return + + def clear(self): + logger.verbose("Clear view history") + self._navHistory = [] + self._navPosition = -1 + return + + def append(self, tHandle): + logger.verbose("Added %s to view history" % tHandle) + return + + def forward(self): + logger.verbose("Move forwards in view history") + return True + + def backward(self): + logger.verbose("Move backwards in view history") + return True + + ## + # Internal Functions + ## + + def _truncateHistory(self, atPos): + return + +# END Class GuiDocViewHistory + # =============================================================================================== # # The Embedded Document Header # Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index b904ffd9..ed96a4ee 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -475,6 +475,23 @@ class GuiMainMenu(QMenuBar): # View > Separator self.viewMenu.addSeparator() + # View > Navigate Viewer Backward + self.aViewPrev = QAction("Navigate Viewer Backward", self) + self.aViewPrev.setStatusTip("Show the previous document in the right pane") + self.aViewPrev.setShortcut("Alt+Left") + self.aViewPrev.triggered.connect(self.theParent.docViewer.navBackward) + self.viewMenu.addAction(self.aViewPrev) + + # View > Navigate Viewer Forward + self.aViewNext = QAction("Navigate Viewer Forward", self) + self.aViewNext.setStatusTip("Show the next document in the right pane") + self.aViewNext.setShortcut("Alt+Right") + self.aViewNext.triggered.connect(self.theParent.docViewer.navForward) + self.viewMenu.addAction(self.aViewNext) + + # View > Separator + self.viewMenu.addSeparator() + # View > Toggle Distraction Free Mode self.aFocusMode = QAction("Distraction Free Mode", self) self.aFocusMode.setStatusTip("Toggles distraction free mode, only showing text editor") From e30064ba0b181bbe923690a5b72da1fcb2fb202d Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 21:20:38 +0200 Subject: [PATCH 3/9] Nav history in doc viewer now works --- nw/gui/docviewer.py | 128 ++++++++++++++++++++++++++++++++++++++++---- nw/guimain.py | 1 + 2 files changed, 118 insertions(+), 11 deletions(-) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index e8333a00..024b1723 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -10,6 +10,7 @@ Created: 2019-10-31 [0.3.2] GuiDocViewDetails Created: 2020-04-25 [0.4.5] GuiDocViewHeader Created: 2020-06-09 [0.8.0] GuiDocViewFooter + Created: 2020-09-08 [1.0b1] GuiDocViewHistory This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen @@ -131,7 +132,7 @@ class GuiDocViewer(QTextBrowser): return True - def loadText(self, tHandle): + def loadText(self, tHandle, updateHistory=True): """Load text into the viewer from an item handle. """ tItem = self.theProject.projTree[tHandle] @@ -169,9 +170,12 @@ class GuiDocViewer(QTextBrowser): else: self.setTabStopWidth(self.mainConf.getTabWidth()) + # Must be before setHtml + if updateHistory: + self.docHistory.append(tHandle) + self.setHtml(aDoc.theResult.replace("\t", "!!tab!!")) self.setDocumentTitle(tHandle) - self.docHistory.append(tHandle) # Loop through the text and put back in the tabs. Tabs are removed by # the setHtml function, so the ToHtml class puts in a placeholder. @@ -266,6 +270,12 @@ class GuiDocViewer(QTextBrowser): self.docHistory.forward() return + def clearNavHistory(self): + """Clear the navigation history. + """ + self.docHistory.clear() + return + def updateDocMargins(self): """Automatically adjust the margins so the text is centred if Config.textFixedW is enabled or we're in Focus Mode. Otherwise, @@ -326,6 +336,26 @@ class GuiDocViewer(QTextBrowser): logger.verbose("Cursor moved to line %d" % theLine) return True + def setScrollPosition(self, thePos): + """Set the scrollbar position. + """ + vBar = self.verticalScrollBar() + if vBar.isVisible(): + vBar.setValue(thePos) + return + + ## + # Getters + ## + + def getScrollPosition(self): + """Get the scrollbar position. Returns 0 if no scrollbar. + """ + vBar = self.verticalScrollBar() + if vBar.isVisible(): + return vBar.value() + return 0 + ## # Slots ## @@ -403,6 +433,8 @@ class GuiDocViewer(QTextBrowser): self.navBackward() elif theEvent.button() == Qt.ForwardButton: self.navForward() + else: + QTextBrowser.mouseReleaseEvent(self, theEvent) return ## @@ -511,35 +543,109 @@ class GuiDocViewHistory(): self.docViewer = docViewer self._navHistory = [] - self._navPosition = -1 + self._posHistory = [] + self._currPos = -1 + self._prevPos = -1 return def clear(self): - logger.verbose("Clear view history") + """Clear the view history. + """ + logger.verbose("View history cleared") self._navHistory = [] - self._navPosition = -1 + self._posHistory = [] + self._currPos = -1 + self._prevPos = -1 return def append(self, tHandle): + """Append a document handle and its scroll bar position to the + history, but only if the document is different than the current + active entry. Any further entries are truncated. + """ + if self._currPos >= 0 and self._currPos < len(self._navHistory): + if tHandle == self._navHistory[self._currPos]: + logger.verbose("Not updating view hsitory") + return False + + self._truncateHistory(self._currPos) + + self._navHistory.append(tHandle) + self._posHistory.append(0) + + self._prevPos = self._currPos + self._currPos = len(self._navHistory) - 1 + self._updateScrollBar() + + self._dumpHistory() + logger.verbose("Added %s to view history" % tHandle) - return + + return True def forward(self): - logger.verbose("Move forwards in view history") - return True + """Navigate to the next entry in the view history. + """ + newPos = self._currPos + 1 + if newPos < len(self._navHistory): + logger.verbose("Move forward in view history") + self._prevPos = self._currPos + self._updateScrollBar() + + self.docViewer.loadText(self._navHistory[newPos], updateHistory=False) + self.docViewer.setScrollPosition(self._posHistory[newPos]) + self._currPos = newPos + + self._dumpHistory() + + return def backward(self): - logger.verbose("Move backwards in view history") - return True + """Navigate to the previous entry in the view history. + """ + newPos = self._currPos - 1 + if newPos >= 0: + logger.verbose("Move backward in view history") + self._prevPos = self._currPos + self._updateScrollBar() + + self.docViewer.loadText(self._navHistory[newPos], updateHistory=False) + self.docViewer.setScrollPosition(self._posHistory[newPos]) + self._currPos = newPos + + self._dumpHistory() + + return ## # Internal Functions ## - def _truncateHistory(self, atPos): + def _updateScrollBar(self): + """Update the scrollbar position of the previous entry. + """ + if self._prevPos >= 0 and self._prevPos < len(self._posHistory): + self._posHistory[self._prevPos] = self.docViewer.getScrollPosition() return + def _truncateHistory(self, atPos): + """Truncate the navigation history to the given position. + """ + nSkip = 1 if atPos > 49 else 0 + + self._navHistory = self._navHistory[nSkip:atPos + 1] + self._posHistory = self._posHistory[nSkip:atPos + 1] + + self._currPos -= nSkip + self._prevPos -= nSkip + + return + + def _dumpHistory(self): + for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory)): + print("%s %3d %13s %6d" % (">" if i == self._currPos else " ", i, h, p)) + # END Class GuiDocViewHistory # =============================================================================================== # diff --git a/nw/guimain.py b/nw/guimain.py index 0729a7df..73209394 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -346,6 +346,7 @@ class GuiMain(QMainWindow): if saveOK: self.closeDocument() + self.docViewer.clearNavHistory() self.projView.closeOutline() self.theProject.closeProject() self.theIndex.clearIndex() From 7bd190726193ba0ae9511a64c03332f6b139be94 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 21:59:44 +0200 Subject: [PATCH 4/9] Added icons for backward and forward navigation buttons --- .../typicons_colour_dark/chevron-left.svg | 31 +++++++++++++++++++ .../typicons_colour_dark/chevron-right.svg | 31 +++++++++++++++++++ .../icons/typicons_colour_dark/icons.conf | 2 ++ .../typicons_colour_light/chevron-left.svg | 31 +++++++++++++++++++ .../typicons_colour_light/chevron-right.svg | 31 +++++++++++++++++++ .../icons/typicons_colour_light/icons.conf | 2 ++ .../icons/typicons_grey_dark/chevron-left.svg | 31 +++++++++++++++++++ .../typicons_grey_dark/chevron-right.svg | 31 +++++++++++++++++++ nw/assets/icons/typicons_grey_dark/icons.conf | 2 ++ .../typicons_grey_light/chevron-left.svg | 31 +++++++++++++++++++ .../typicons_grey_light/chevron-right.svg | 31 +++++++++++++++++++ .../icons/typicons_grey_light/icons.conf | 2 ++ 12 files changed, 256 insertions(+) create mode 100644 nw/assets/icons/typicons_colour_dark/chevron-left.svg create mode 100644 nw/assets/icons/typicons_colour_dark/chevron-right.svg create mode 100644 nw/assets/icons/typicons_colour_light/chevron-left.svg create mode 100644 nw/assets/icons/typicons_colour_light/chevron-right.svg create mode 100644 nw/assets/icons/typicons_grey_dark/chevron-left.svg create mode 100644 nw/assets/icons/typicons_grey_dark/chevron-right.svg create mode 100644 nw/assets/icons/typicons_grey_light/chevron-left.svg create mode 100644 nw/assets/icons/typicons_grey_light/chevron-right.svg diff --git a/nw/assets/icons/typicons_colour_dark/chevron-left.svg b/nw/assets/icons/typicons_colour_dark/chevron-left.svg new file mode 100644 index 00000000..df4d0635 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/chevron-left.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/chevron-right.svg b/nw/assets/icons/typicons_colour_dark/chevron-right.svg new file mode 100644 index 00000000..b5671fa7 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/chevron-right.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/icons.conf b/nw/assets/icons/typicons_colour_dark/icons.conf index 8432bf1a..a5930cfc 100644 --- a/nw/assets/icons/typicons_colour_dark/icons.conf +++ b/nw/assets/icons/typicons_colour_dark/icons.conf @@ -51,5 +51,7 @@ check = tick.svg cross = times.svg hash = hash.svg reference = at.svg +backward = chevron-left.svg +forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/assets/icons/typicons_colour_light/chevron-left.svg b/nw/assets/icons/typicons_colour_light/chevron-left.svg new file mode 100644 index 00000000..6125533e --- /dev/null +++ b/nw/assets/icons/typicons_colour_light/chevron-left.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_light/chevron-right.svg b/nw/assets/icons/typicons_colour_light/chevron-right.svg new file mode 100644 index 00000000..b4f300e1 --- /dev/null +++ b/nw/assets/icons/typicons_colour_light/chevron-right.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_light/icons.conf b/nw/assets/icons/typicons_colour_light/icons.conf index eb0f9b2e..934ad1d6 100644 --- a/nw/assets/icons/typicons_colour_light/icons.conf +++ b/nw/assets/icons/typicons_colour_light/icons.conf @@ -51,5 +51,7 @@ check = tick.svg cross = times.svg hash = hash.svg reference = at.svg +backward = chevron-left.svg +forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/assets/icons/typicons_grey_dark/chevron-left.svg b/nw/assets/icons/typicons_grey_dark/chevron-left.svg new file mode 100644 index 00000000..bc1c987f --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/chevron-left.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/chevron-right.svg b/nw/assets/icons/typicons_grey_dark/chevron-right.svg new file mode 100644 index 00000000..18eafe98 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/chevron-right.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/icons.conf b/nw/assets/icons/typicons_grey_dark/icons.conf index 5fa6d680..81829f57 100644 --- a/nw/assets/icons/typicons_grey_dark/icons.conf +++ b/nw/assets/icons/typicons_grey_dark/icons.conf @@ -51,5 +51,7 @@ check = tick.svg cross = times.svg hash = hash.svg reference = at.svg +backward = chevron-left.svg +forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/assets/icons/typicons_grey_light/chevron-left.svg b/nw/assets/icons/typicons_grey_light/chevron-left.svg new file mode 100644 index 00000000..2e7af570 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/chevron-left.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/chevron-right.svg b/nw/assets/icons/typicons_grey_light/chevron-right.svg new file mode 100644 index 00000000..8da983ca --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/chevron-right.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/icons.conf b/nw/assets/icons/typicons_grey_light/icons.conf index 4363e16f..145dc153 100644 --- a/nw/assets/icons/typicons_grey_light/icons.conf +++ b/nw/assets/icons/typicons_grey_light/icons.conf @@ -51,5 +51,7 @@ check = tick.svg cross = times.svg hash = hash.svg reference = at.svg +backward = chevron-left.svg +forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg From a7a345f05cb3fde0ba48f8371eb420cdd87be074 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 22:00:07 +0200 Subject: [PATCH 5/9] Added backward and forward nav buttons --- nw/gui/docviewer.py | 59 +++++++++++++++++++++++++++++++++++++++++---- nw/gui/theme.py | 2 ++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 024b1723..e075f80b 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -577,6 +577,7 @@ class GuiDocViewHistory(): self._prevPos = self._currPos self._currPos = len(self._navHistory) - 1 self._updateScrollBar() + self._updateNavButtons() self._dumpHistory() @@ -596,6 +597,7 @@ class GuiDocViewHistory(): self.docViewer.loadText(self._navHistory[newPos], updateHistory=False) self.docViewer.setScrollPosition(self._posHistory[newPos]) self._currPos = newPos + self._updateNavButtons() self._dumpHistory() @@ -613,6 +615,7 @@ class GuiDocViewHistory(): self.docViewer.loadText(self._navHistory[newPos], updateHistory=False) self.docViewer.setScrollPosition(self._posHistory[newPos]) self._currPos = newPos + self._updateNavButtons() self._dumpHistory() @@ -629,10 +632,16 @@ class GuiDocViewHistory(): self._posHistory[self._prevPos] = self.docViewer.getScrollPosition() return + def _updateNavButtons(self): + """Update the navigation buttons in the document header. + """ + self.docViewer.docHeader.updateNavButtons(0, len(self._navHistory) - 1, self._currPos) + return + def _truncateHistory(self, atPos): """Truncate the navigation history to the given position. """ - nSkip = 1 if atPos > 49 else 0 + nSkip = 1 if atPos > 19 else 0 self._navHistory = self._navHistory[nSkip:atPos + 1] self._posHistory = self._posHistory[nSkip:atPos + 1] @@ -643,8 +652,15 @@ class GuiDocViewHistory(): return def _dumpHistory(self): - for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory)): - print("%s %3d %13s %6d" % (">" if i == self._currPos else " ", i, h, p)) + """Debug function to dump history. Since it is a for loop, it is + skipped entirely if log level isn't VERBOSE. + """ + if logger.getEffectiveLevel() < logging.DEBUG: + for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory)): + logger.verbose( + "History: %s %2d %13s %5d" % (">" if i == self._currPos else " ", i, h, p) + ) + return # END Class GuiDocViewHistory @@ -674,10 +690,8 @@ class GuiDocViewHeader(QWidget): fPx = int(0.9*self.theTheme.fontPixelSize) hSp = self.mainConf.pxInt(6) - self.buttonSize = fPx + hSp # Main Widget Settings - self.setContentsMargins(2*self.buttonSize, 0, 0, 0) self.setAutoFillBackground(True) self.setPalette(self.thePalette) @@ -702,6 +716,28 @@ class GuiDocViewHeader(QWidget): ).format(*self.theTheme.colText) # Buttons + self.backButton = QToolButton(self) + self.backButton.setIcon(self.theTheme.getIcon("backward")) + self.backButton.setContentsMargins(0, 0, 0, 0) + self.backButton.setIconSize(QSize(fPx, fPx)) + self.backButton.setFixedSize(fPx, fPx) + self.backButton.setStyleSheet(buttonStyle) + self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.backButton.setVisible(False) + self.backButton.setToolTip("Go backward") + self.backButton.clicked.connect(self.docViewer.navBackward) + + self.forwardButton = QToolButton(self) + self.forwardButton.setIcon(self.theTheme.getIcon("forward")) + self.forwardButton.setContentsMargins(0, 0, 0, 0) + self.forwardButton.setIconSize(QSize(fPx, fPx)) + self.forwardButton.setFixedSize(fPx, fPx) + self.forwardButton.setStyleSheet(buttonStyle) + self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.forwardButton.setVisible(False) + self.forwardButton.setToolTip("Go forward") + self.forwardButton.clicked.connect(self.docViewer.navForward) + self.refreshButton = QToolButton(self) self.refreshButton.setIcon(self.theTheme.getIcon("refresh")) self.refreshButton.setContentsMargins(0, 0, 0, 0) @@ -727,6 +763,8 @@ class GuiDocViewHeader(QWidget): # Assemble Layout self.outerBox = QHBoxLayout() self.outerBox.setSpacing(hSp) + self.outerBox.addWidget(self.backButton, 0) + self.outerBox.addWidget(self.forwardButton, 0) self.outerBox.addWidget(self.theTitle, 1) self.outerBox.addWidget(self.refreshButton, 0) self.outerBox.addWidget(self.closeButton, 0) @@ -747,6 +785,8 @@ class GuiDocViewHeader(QWidget): self.theHandle = tHandle if tHandle is None: self.theTitle.setText("") + self.backButton.setVisible(False) + self.forwardButton.setVisible(False) self.closeButton.setVisible(False) self.refreshButton.setVisible(False) return True @@ -766,11 +806,20 @@ class GuiDocViewHeader(QWidget): return False self.theTitle.setText(nwItem.itemName) + self.backButton.setVisible(True) + self.forwardButton.setVisible(True) self.closeButton.setVisible(True) self.refreshButton.setVisible(True) return True + def updateNavButtons(self, firstIdx, lastIdx, currIdx): + """Enable and disable nav buttons based on index in history. + """ + self.backButton.setEnabled(currIdx > firstIdx) + self.forwardButton.setEnabled(currIdx < lastIdx) + return + ## # Slots ## diff --git a/nw/gui/theme.py b/nw/gui/theme.py index fd2826a0..9abfb523 100644 --- a/nw/gui/theme.py +++ b/nw/gui/theme.py @@ -554,6 +554,8 @@ class GuiIcons: "minimise" : (None, None), "refresh" : (None, None), "reference" : (None, None), + "backward" : (None, None), + "forward" : (None, None), ## Switches "sticky-on" : (None, None), From d8aa29f656dc08a4dd9e09057f458f25bc01e110 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 22:10:08 +0200 Subject: [PATCH 6/9] Added test coverage of navigation buttons and class --- tests/test_gui.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_gui.py b/tests/test_gui.py index bdb684ff..524e74c9 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -1163,6 +1163,22 @@ def testDocAction(qtbot, nwLipsum, nwTemp): "nunc lacus, imperdiet nec posuere ac, interdum non lectus." ) + # Navigation History + assert nwGUI.viewDocument("04468803b92e1") + assert nwGUI.docViewer.theHandle == "04468803b92e1" + assert nwGUI.docViewer.docHeader.backButton.isEnabled() + assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() + + qtbot.mouseClick(nwGUI.docViewer.docHeader.backButton, Qt.LeftButton) + assert nwGUI.docViewer.theHandle == "4c4f28287af27" + assert not nwGUI.docViewer.docHeader.backButton.isEnabled() + assert nwGUI.docViewer.docHeader.forwardButton.isEnabled() + + qtbot.mouseClick(nwGUI.docViewer.docHeader.forwardButton, Qt.LeftButton) + assert nwGUI.docViewer.theHandle == "04468803b92e1" + assert nwGUI.docViewer.docHeader.backButton.isEnabled() + assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() + # qtbot.stopForInteraction() nwGUI.closeMain() From b67a518b9897db7f06a0caf8a11863cf365f24c1 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 22:25:37 +0200 Subject: [PATCH 7/9] Renamed menu entries and updated docs --- docs/source/int_interface.rst | 2 ++ nw/gui/mainmenu.py | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/source/int_interface.rst b/docs/source/int_interface.rst index 3c4b772a..599757dc 100644 --- a/docs/source/int_interface.rst +++ b/docs/source/int_interface.rst @@ -328,6 +328,8 @@ Most features are available as keyboard shortcuts. These are as follows: ":kbd:`Alt`:kbd:`1`", "Switch focus to the project tree." ":kbd:`Alt`:kbd:`2`", "Switch focus to document editor." ":kbd:`Alt`:kbd:`3`", "Switch focus to document viewer." + ":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer." + ":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer." ":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor." ":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog." ":kbd:`Ctrl`:kbd:`/`", "Change block format to comment." diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index ed96a4ee..9c4ce12c 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -475,16 +475,16 @@ class GuiMainMenu(QMenuBar): # View > Separator self.viewMenu.addSeparator() - # View > Navigate Viewer Backward - self.aViewPrev = QAction("Navigate Viewer Backward", self) - self.aViewPrev.setStatusTip("Show the previous document in the right pane") + # View > Go Backward + self.aViewPrev = QAction("Go Backward", self) + self.aViewPrev.setStatusTip("Move backward in the view history of the right pane") self.aViewPrev.setShortcut("Alt+Left") self.aViewPrev.triggered.connect(self.theParent.docViewer.navBackward) self.viewMenu.addAction(self.aViewPrev) - # View > Navigate Viewer Forward - self.aViewNext = QAction("Navigate Viewer Forward", self) - self.aViewNext.setStatusTip("Show the next document in the right pane") + # View > Go Forward + self.aViewNext = QAction("Go Forward", self) + self.aViewNext.setStatusTip("Move forward in the view history of the right pane") self.aViewNext.setShortcut("Alt+Right") self.aViewNext.triggered.connect(self.theParent.docViewer.navForward) self.viewMenu.addAction(self.aViewNext) From ad8d3845375b2f7652b7e2fef662440a4138a8c6 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 22:42:18 +0200 Subject: [PATCH 8/9] Add fallback icons --- nw/assets/icons/fallback/backward-dark.svg | 31 ++++++++++++++++++++++ nw/assets/icons/fallback/backward.svg | 31 ++++++++++++++++++++++ nw/assets/icons/fallback/forward-dark.svg | 31 ++++++++++++++++++++++ nw/assets/icons/fallback/forward.svg | 31 ++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 nw/assets/icons/fallback/backward-dark.svg create mode 100644 nw/assets/icons/fallback/backward.svg create mode 100644 nw/assets/icons/fallback/forward-dark.svg create mode 100644 nw/assets/icons/fallback/forward.svg diff --git a/nw/assets/icons/fallback/backward-dark.svg b/nw/assets/icons/fallback/backward-dark.svg new file mode 100644 index 00000000..bc1c987f --- /dev/null +++ b/nw/assets/icons/fallback/backward-dark.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/fallback/backward.svg b/nw/assets/icons/fallback/backward.svg new file mode 100644 index 00000000..2e7af570 --- /dev/null +++ b/nw/assets/icons/fallback/backward.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/fallback/forward-dark.svg b/nw/assets/icons/fallback/forward-dark.svg new file mode 100644 index 00000000..18eafe98 --- /dev/null +++ b/nw/assets/icons/fallback/forward-dark.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/fallback/forward.svg b/nw/assets/icons/fallback/forward.svg new file mode 100644 index 00000000..8da983ca --- /dev/null +++ b/nw/assets/icons/fallback/forward.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + From 6067c9af0e85361fa2c11e14f362ce701760d4fc Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 8 Sep 2020 23:13:49 +0200 Subject: [PATCH 9/9] Middle mouse clicking a document in the tree view opens it for viewing --- nw/gui/projtree.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 2eaf832c..36130b1f 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -662,13 +662,28 @@ class GuiProjectTree(QTreeWidget): def mousePressEvent(self, theEvent): """Overload mousePressEvent to clear selection if clicking the - mouse in a blank area of the tree view. + mouse in a blank area of the tree view, and to load a document + for viewing if the suer middle clicked. """ QTreeWidget.mousePressEvent(self, theEvent) - selItem = self.indexAt(theEvent.pos()) - if not selItem.isValid(): - self.clearSelection() + if theEvent.button() == Qt.LeftButton: + selItem = self.indexAt(theEvent.pos()) + if not selItem.isValid(): + self.clearSelection() + + elif theEvent.button() == Qt.MiddleButton: + selItem = self.itemAt(theEvent.pos()) + if not isinstance(selItem, QTreeWidgetItem): + return + + tHandle = selItem.data(self.C_NAME, Qt.UserRole) + tItem = self.theProject.projTree[tHandle] + if tItem is None: + return + + if tItem.itemType == nwItemType.FILE: + self.theParent.viewDocument(tHandle) return