From 29950e34a4f637f445af05f9f56ea8e9c3929b87 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Wed, 10 Jun 2020 17:19:52 +0200 Subject: [PATCH 1/8] Some completeness in logger output --- nw/__init__.py | 4 +++- nw/config.py | 3 +++ nw/core/project.py | 1 + nw/core/spellcheck.py | 4 ++-- nw/gui/docbars.py | 4 ++-- nw/gui/dochighlight.py | 4 ++-- nw/gui/projtree.py | 4 ++++ nw/gui/statusbar.py | 4 ++-- nw/gui/theme.py | 4 ++-- nw/guimain.py | 5 ++++- 10 files changed, 25 insertions(+), 12 deletions(-) diff --git a/nw/__init__.py b/nw/__init__.py index 0d1c2088..98436cca 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -216,7 +216,9 @@ def main(sysArgs=None): logger.addHandler(cHandle) logger.setLevel(debugLevel) - logger.info("This is %s %s" % (__package__, __version__)) + logger.info("Starting %s" % __package__) + logger.info("Version: %s (%s)" % (__version__, __hexversion__)) + logger.info("Last Release: %s" % __date__) # Check Packages and Versions errorData = [] diff --git a/nw/config.py b/nw/config.py index 5c734981..a1e222dd 100644 --- a/nw/config.py +++ b/nw/config.py @@ -222,6 +222,7 @@ class Config: """Initialise the config class. The manual setting of confPath and dataPath is mainly intended for the test suite. """ + logger.debug("Initialising Config ...") if confPath is None: confRoot = QStandardPaths.writableLocation(QStandardPaths.ConfigLocation) self.confPath = path.join(path.abspath(confRoot), self.appHandle) @@ -304,6 +305,8 @@ class Config: if self.spellLanguage is None: self.spellLanguage = "en" + logger.debug("Config initialisation complete") + return True def loadConfig(self): diff --git a/nw/core/project.py b/nw/core/project.py index caa81b09..0784fc7a 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -1041,6 +1041,7 @@ class NWProject(): return # Then check the files in the data folder + logger.debug("Checking files in project content folder") orphanFiles = [] for fileItem in listdir(self.projContent): if not fileItem.endswith(".nwd"): diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py index 6296bc60..44d0fc38 100644 --- a/nw/core/spellcheck.py +++ b/nw/core/spellcheck.py @@ -115,12 +115,12 @@ class NWSpellCheck(): if not path.isfile(projectDict): return try: - with open(projectDict,mode="r",encoding="utf-8") as wordsFile: + logger.debug("Loading project word list") + with open(projectDict, mode="r", encoding="utf-8") as wordsFile: for theLine in wordsFile: theLine = theLine.strip() if len(theLine) > 0 and theLine not in self.PROJW: self.PROJW.append(theLine) - logger.debug("Project word list") logger.debug("Project word list contains %d words" % len(self.PROJW)) except Exception as e: logger.error("Failed to load project word list") diff --git a/nw/gui/docbars.py b/nw/gui/docbars.py index 839ae024..f7406c17 100644 --- a/nw/gui/docbars.py +++ b/nw/gui/docbars.py @@ -477,7 +477,7 @@ class GuiDocViewDetails(QScrollArea): def __init__(self, theParent): QScrollArea.__init__(self, theParent) - logger.debug("Initialising DocViewDetails ...") + logger.debug("Initialising GuiDocViewDetails ...") self.mainConf = nw.CONFIG self.theParent = theParent self.theProject = theParent.theProject @@ -508,7 +508,7 @@ class GuiDocViewDetails(QScrollArea): self.setWidgetResizable(True) self.setMinimumHeight(self.mainConf.pxInt(50)) - logger.debug("DocViewDetails initialisation complete") + logger.debug("GuiDocViewDetails initialisation complete") return diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index cda6ac03..afd5a77c 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -42,7 +42,7 @@ class GuiDocHighlighter(QSyntaxHighlighter): def __init__(self, theDoc, theParent): QSyntaxHighlighter.__init__(self, theDoc) - logger.debug("Initialising DocHighlighter ...") + logger.debug("Initialising GuiDocHighlighter ...") self.mainConf = nw.CONFIG self.theDoc = theDoc self.theParent = theParent @@ -70,7 +70,7 @@ class GuiDocHighlighter(QSyntaxHighlighter): self.initHighlighter() - logger.debug("DocHighlighter initialisation complete") + logger.debug("GuiDocHighlighter initialisation complete") return diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index b7b9029c..02e850ad 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -525,9 +525,13 @@ class GuiProjectTree(QTreeWidget): always make sure items with a parent have had their parent item sent first. """ + logger.debug("Building project tree ...") self.clear() + iCount = 0 for nwItem in self.theProject.getProjectItems(): + iCount += 1 self._addTreeItem(nwItem) + logger.debug("%d items added to project tree" % iCount) return True def getSelectedHandle(self): diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index a7fbc342..a40e4022 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -44,7 +44,7 @@ class GuiMainStatus(QStatusBar): def __init__(self, theParent): QStatusBar.__init__(self, theParent) - logger.debug("Initialising MainStatus ...") + logger.debug("Initialising GuiMainStatus ...") self.mainConf = nw.CONFIG self.theParent = theParent @@ -123,7 +123,7 @@ class GuiMainStatus(QStatusBar): self.sessionTimer.timeout.connect(self._updateTime) self.sessionTimer.start() - logger.debug("MainStatus initialisation complete") + logger.debug("GuiMainStatus initialisation complete") self.clearStatus() diff --git a/nw/gui/theme.py b/nw/gui/theme.py index 78b2b44d..98c9a7b9 100644 --- a/nw/gui/theme.py +++ b/nw/gui/theme.py @@ -750,11 +750,11 @@ class GuiIcons: self.mainConf.iconPath, self.fbackName, "%s-dark.svg" % iconKey ) if path.isfile(fbackIcon): - logger.verbose("Loading icon '%s' from fallback theme" % iconKey) + logger.verbose("Loading icon '%s' from fallback theme (dark mode)" % iconKey) return QIcon(fbackIcon) fbackIcon = path.join(self.mainConf.iconPath, self.fbackName, "%s.svg" % iconKey) if path.isfile(fbackIcon): - logger.verbose("Loading icon '%s' from fallback theme" % iconKey) + logger.verbose("Loading icon '%s' from fallback theme (light mode)" % iconKey) return QIcon(fbackIcon) # Give up and return an empty icon diff --git a/nw/guimain.py b/nw/guimain.py index e34ff448..576cd990 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -55,7 +55,6 @@ class GuiMain(QMainWindow): def __init__(self): QMainWindow.__init__(self) - logger.info("Starting %s" % nw.__package__) logger.debug("Initialising GUI ...") self.mainConf = nw.CONFIG @@ -232,6 +231,8 @@ class GuiMain(QMainWindow): else: self.manageProjects() + logger.debug("%s is ready ..." % nw.__package__) + return def clearGUI(self): @@ -435,6 +436,8 @@ class GuiMain(QMainWindow): if self.theIndex.indexBroken: self.rebuildIndex() + logger.debug("Project load complete") + return True def saveProject(self, autoSave=False): From e58c00b610a42dbc1a86ce377bb90b7ebdc8c328 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 16:33:57 +0200 Subject: [PATCH 2/8] Use frameWidth instead of lineWidth for positioning document title bars --- nw/gui/doceditor.py | 2 +- nw/gui/docviewer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index d3b6eb93..fadd6dd5 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -333,7 +333,7 @@ class GuiDocEditor(QTextEdit): else: tM = cM - tB = self.lineWidth() + tB = self.frameWidth() tW = self.width() - 2*tB - sW tH = self.docTitle.height() tT = cM - tH diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 0648111b..42332fb5 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -230,7 +230,7 @@ class GuiDocViewer(QTextBrowser): else: sW = 0 - tB = self.lineWidth() + tB = self.frameWidth() tW = self.width() - 2*tB - sW tH = self.docTitle.height() fH = self.docFooter.height() From e4f2949d3d9bacf8f0865594c596f1d0c193d66b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 16:47:54 +0200 Subject: [PATCH 3/8] All the DocViewer elements now live in the docviewer file --- nw/gui/__init__.py | 6 +- nw/gui/custom.py | 2 +- nw/gui/docbars.py | 197 ---------------------- nw/gui/docviewer.py | 389 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 380 insertions(+), 214 deletions(-) diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py index 4073dcc6..8800834d 100644 --- a/nw/gui/__init__.py +++ b/nw/gui/__init__.py @@ -2,11 +2,11 @@ from nw.gui.about import GuiAbout from nw.gui.build import GuiBuildNovel -from nw.gui.docbars import GuiDocTitleBar, GuiSearchBar, GuiDocViewDetails +from nw.gui.docbars import GuiDocTitleBar, GuiSearchBar from nw.gui.doceditor import GuiDocEditor from nw.gui.docmerge import GuiDocMerge from nw.gui.docsplit import GuiDocSplit -from nw.gui.docviewer import GuiDocViewer +from nw.gui.docviewer import GuiDocViewer, GuiDocViewDetails from nw.gui.itemdetails import GuiItemDetails from nw.gui.itemeditor import GuiItemEditor from nw.gui.mainmenu import GuiMainMenu @@ -25,11 +25,11 @@ __all__ = [ "GuiBuildNovel", "GuiDocTitleBar", "GuiSearchBar", - "GuiDocViewDetails", "GuiDocEditor", "GuiDocMerge", "GuiDocSplit", "GuiDocViewer", + "GuiDocViewDetails", "GuiItemDetails", "GuiItemEditor", "GuiMainMenu", diff --git a/nw/gui/custom.py b/nw/gui/custom.py index 264a2c98..5c3dfbd6 100644 --- a/nw/gui/custom.py +++ b/nw/gui/custom.py @@ -45,7 +45,7 @@ from nw.constants import nwUnicode logger = logging.getLogger(__name__) # =============================================================================================== # -# Config FOrm Layout +# Config Form Layout # =============================================================================================== # class QConfigLayout(QGridLayout): diff --git a/nw/gui/docbars.py b/nw/gui/docbars.py index f7406c17..fcbc1869 100644 --- a/nw/gui/docbars.py +++ b/nw/gui/docbars.py @@ -7,9 +7,7 @@ File History: Created: 2019-09-29 [0.2.1] GuiSearchBar - Created: 2019-10-31 [0.3.2] GuiDocViewDetails Created: 2020-04-25 [0.4.5] GuiDocTitleBar - Created: 2020-06-09 [0.8.0] GuiDocViewFooter This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen @@ -353,198 +351,3 @@ class GuiDocTitleBar(QWidget): return # END Class GuiDocTitleBar - -class GuiDocViewFooter(QWidget): - - def __init__(self, theParent): - QWidget.__init__(self, theParent) - - logger.debug("Initialising GuiDocViewFooter ...") - - self.mainConf = nw.CONFIG - self.theParent = theParent - self.theTheme = theParent.theTheme - self.optState = theParent.theProject.optState - self.viewMeta = theParent.theParent.viewMeta - self.theHandle = None - - # Make a QPalette that matches the Syntax Theme - self.thePalette = QPalette() - self.thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) - self.thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) - - fPx = int(0.9*self.theTheme.fontPixelSize) - hSp = self.mainConf.pxInt(6) - - # Main Widget Settings - self.setContentsMargins(0, 0, 0, 0) - self.setAutoFillBackground(True) - self.setPalette(self.thePalette) - - buttonStyle = ( - "QToolButton {{border: none; background: transparent;}} " - "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" - ).format(*self.theTheme.colText) - - # Show/Hide Details - self.showHide = QToolButton(self) - self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.showHide.setStyleSheet(buttonStyle) - self.showHide.setIcon(self.theTheme.getIcon("reference")) - self.showHide.setIconSize(QSize(fPx, fPx)) - self.showHide.setFixedSize(QSize(fPx, fPx)) - self.showHide.clicked.connect(self._doShowHide) - - # Sticky Button - stickyOn = self.theTheme.getPixmap("sticky-on", (fPx, fPx)) - stickyOff = self.theTheme.getPixmap("sticky-off", (fPx, fPx)) - stickyIcon = QIcon() - stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On) - stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off) - self.stickyRefs = QToolButton(self) - self.stickyRefs.setCheckable(True) - self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.stickyRefs.setStyleSheet(buttonStyle) - self.stickyRefs.setIcon(stickyIcon) - self.stickyRefs.setIconSize(QSize(fPx, fPx)) - self.stickyRefs.setFixedSize(QSize(fPx, fPx)) - self.stickyRefs.toggled.connect(self._doToggleSticky) - - # Labels - self.lblRefs = QLabel("References") - self.lblRefs.setBuddy(self.showHide) - self.lblRefs.setIndent(0) - self.lblRefs.setMargin(0) - self.lblRefs.setContentsMargins(0, 0, 0, 0) - self.lblRefs.setAutoFillBackground(True) - self.lblRefs.setFixedHeight(fPx) - self.lblRefs.setAlignment(Qt.AlignLeft | Qt.AlignTop) - self.lblRefs.setPalette(self.thePalette) - - self.lblSticky = QLabel("Sticky") - self.lblSticky.setBuddy(self.stickyRefs) - self.lblSticky.setIndent(0) - self.lblSticky.setMargin(0) - self.lblSticky.setContentsMargins(0, 0, 0, 0) - self.lblSticky.setAutoFillBackground(True) - self.lblSticky.setFixedHeight(fPx) - self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop) - self.lblSticky.setPalette(self.thePalette) - - lblFont = self.font() - lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) - self.lblRefs.setFont(lblFont) - self.lblSticky.setFont(lblFont) - - # Assemble Layout - self.outerBox = QHBoxLayout() - self.outerBox.setSpacing(hSp) - self.outerBox.addWidget(self.showHide, 0) - self.outerBox.addWidget(self.lblRefs, 0) - self.outerBox.addWidget(self.stickyRefs, 0) - self.outerBox.addWidget(self.lblSticky, 0) - self.outerBox.addStretch(1) - self.setLayout(self.outerBox) - - logger.debug("GuiDocViewFooter initialisation complete") - - return - - ## - # Slots - ## - - def _doShowHide(self): - """Toggle the expand/collapse of the panel. - """ - isVisible = self.viewMeta.isVisible() - self.viewMeta.setVisible(not isVisible) - return - - def _doToggleSticky(self, theState): - """Toggle the sticky flag for the reference panel. - """ - logger.verbose("Reference sticky is %s" % str(theState)) - self.theParent.stickyRef = theState - if not theState and self.theParent.theHandle is not None: - self.viewMeta.refreshReferences(self.theParent.theHandle) - return - -# END Class GuiDocViewFooter - -class GuiDocViewDetails(QScrollArea): - - def __init__(self, theParent): - QScrollArea.__init__(self, theParent) - - logger.debug("Initialising GuiDocViewDetails ...") - self.mainConf = nw.CONFIG - self.theParent = theParent - self.theProject = theParent.theProject - self.theTheme = theParent.theTheme - self.currHandle = None - - self.refList = QLabel("None") - self.refList.setWordWrap(True) - self.refList.setAlignment(Qt.AlignTop) - self.refList.setScaledContents(True) - self.refList.linkActivated.connect(self._linkClicked) - - hCol = self.palette().highlight().color() - self.linkStyle = "style='color: rgb({0},{1},{2})'".format( - *self.theTheme.colLink - ) - - # Assemble - self.outerWidget = QWidget() - self.outerBox = QHBoxLayout() - self.outerBox.addWidget(self.refList, 1) - - self.outerWidget.setLayout(self.outerBox) - self.setWidget(self.outerWidget) - - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.setWidgetResizable(True) - self.setMinimumHeight(self.mainConf.pxInt(50)) - - logger.debug("GuiDocViewDetails initialisation complete") - - return - - def refreshReferences(self, tHandle): - """Update the current list of document references from the - project index. - """ - self.currHandle = tHandle - if self.theParent.docViewer.stickyRef: - return - - theRefs = self.theParent.theIndex.getBackReferenceList(tHandle) - theList = [] - for tHandle in theRefs: - tItem = self.theProject.projTree[tHandle] - if tItem is not None: - theList.append("%s" % ( - tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName - )) - - self.refList.setText(", ".join(theList)) - - return - - ## - # Internal Functions - ## - - def _linkClicked(self, theLink): - """Capture the link-click and forward it to the document viewer - class for handling. - """ - logger.verbose("Clicked link: '%s'" % theLink) - if len(theLink) == 27: - tHandle = theLink[6:19] - self.theParent.viewDocument(tHandle, theLink) - return - -# END Class GuiDocViewDetails diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 42332fb5..74b20fc7 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -6,7 +6,10 @@ Class holding the document html viewer File History: - Created: 2019-05-10 [0.0.1] + Created: 2019-05-10 [0.0.1] GuiDocViewer + Created: 2019-10-31 [0.3.2] GuiDocViewDetails + Created: 2020-04-25 [0.4.5] GuiDocViewHeader + Created: 2020-06-09 [0.8.0] GuiDocViewFooter This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen @@ -28,13 +31,17 @@ import logging import nw -from PyQt5.QtCore import Qt, QUrl -from PyQt5.QtWidgets import QTextBrowser -from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor, QTextCursor +from PyQt5.QtCore import Qt, QUrl, QSize +from PyQt5.QtGui import ( + QTextOption, QFont, QPalette, QColor, QTextCursor, QIcon +) +from PyQt5.QtWidgets import ( + QTextBrowser, QWidget, QScrollArea, QLabel, QHBoxLayout, QFrame, + QToolButton +) from nw.core import ToHtml -from nw.constants import nwAlert, nwItemType, nwDocAction -from nw.gui.docbars import GuiDocTitleBar, GuiDocViewFooter +from nw.constants import nwAlert, nwItemType, nwDocAction, nwUnicode logger = logging.getLogger(__name__) @@ -57,8 +64,8 @@ class GuiDocViewer(QTextBrowser): self.setOpenExternalLinks(False) self.initViewer() - # Document Title and Footer - self.docTitle = GuiDocTitleBar(self, isEditor=False) + # Document Header and Footer + self.docHeader = GuiDocViewHeader(self) self.docFooter = GuiDocViewFooter(self) self.stickyRef = False @@ -83,7 +90,7 @@ class GuiDocViewer(QTextBrowser): self.clear() self.setSearchPaths([""]) self.theHandle = None - self.docTitle.setTitleFromHandle(self.theHandle) + self.docHeader.setTitleFromHandle(self.theHandle) return True def initViewer(self): @@ -156,7 +163,7 @@ class GuiDocViewer(QTextBrowser): self.verticalScrollBar().setValue(sPos) self.theHandle = tHandle self.theProject.setLastViewed(tHandle) - self.docTitle.setTitleFromHandle(self.theHandle) + self.docHeader.setTitleFromHandle(self.theHandle) self.updateDocMargins() # Make sure the main GUI knows we changed the content @@ -232,12 +239,12 @@ class GuiDocViewer(QTextBrowser): tB = self.frameWidth() tW = self.width() - 2*tB - sW - tH = self.docTitle.height() + tH = self.docHeader.height() fH = self.docFooter.height() fY = self.height() - fH - tB tT = self.mainConf.getTextMargin() - tH bT = self.mainConf.getTextMargin() - fH - self.docTitle.setGeometry(tB, tB, tW, tH) + self.docHeader.setGeometry(tB, tB, tW, tH) self.docFooter.setGeometry(tB, fY, tW, fH) self.setViewportMargins(0, tH, 0, fH) @@ -256,7 +263,7 @@ class GuiDocViewer(QTextBrowser): title bar needs updating, """ if tHandle == self.theHandle: - self.docTitle.setTitleFromHandle(self.theHandle) + self.docHeader.setTitleFromHandle(self.theHandle) self.updateDocMargins() return @@ -387,3 +394,359 @@ class GuiDocViewer(QTextBrowser): return True # END Class GuiDocViewer + +# =============================================================================================== # +# The Embedded Document Header +# Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport +# =============================================================================================== # + +class GuiDocViewHeader(QWidget): + + def __init__(self, theParent): + QWidget.__init__(self, theParent) + + logger.debug("Initialising GuiDocViewHeader ...") + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theParent.theProject + self.theTheme = theParent.theTheme + self.theHandle = None + + # Make a QPalette that matches the Syntax Theme + self.thePalette = QPalette() + self.thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) + self.thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) + + 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) + + # Title Label + self.theTitle = QLabel() + self.theTitle.setText("") + self.theTitle.setIndent(0) + self.theTitle.setMargin(0) + self.theTitle.setContentsMargins(0, 0, 0, 0) + self.theTitle.setAutoFillBackground(True) + self.theTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop) + self.theTitle.setFixedHeight(fPx) + self.theTitle.setFrameShape(QFrame.NoFrame) + self.theTitle.setLineWidth(0) + self.theTitle.setPalette(self.thePalette) + + lblFont = self.theTitle.font() + lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) + self.theTitle.setFont(lblFont) + + buttonStyle = ( + "QToolButton {{border: none; background: transparent;}} " + "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" + ).format(*self.theTheme.colText) + + # Buttons + self.closeButton = QToolButton(self) + self.closeButton.setIcon(self.theTheme.getIcon("close")) + self.closeButton.setContentsMargins(0, 0, 0, 0) + self.closeButton.setIconSize(QSize(fPx, fPx)) + self.closeButton.setFixedSize(fPx, fPx) + self.closeButton.setStyleSheet(buttonStyle) + self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.closeButton.setVisible(False) + self.closeButton.clicked.connect(self._closeDocument) + + self.refreshButton = QToolButton(self) + self.refreshButton.setIcon(self.theTheme.getIcon("refresh")) + self.refreshButton.setContentsMargins(0, 0, 0, 0) + self.refreshButton.setIconSize(QSize(fPx, fPx)) + self.refreshButton.setFixedSize(fPx, fPx) + self.refreshButton.setStyleSheet(buttonStyle) + self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.refreshButton.setVisible(False) + self.refreshButton.clicked.connect(self._refreshDocument) + + # Assemble Layout + self.outerBox = QHBoxLayout() + self.outerBox.setSpacing(hSp) + self.outerBox.addWidget(self.theTitle, 1) + self.outerBox.addWidget(self.refreshButton, 0) + self.outerBox.addWidget(self.closeButton, 0) + self.setLayout(self.outerBox) + + logger.debug("GuiDocViewHeader initialisation complete") + + return + + ## + # Setters + ## + + def setTitleFromHandle(self, tHandle): + """Sets the document title from the handle, or alternatively, + set the whole document path. + """ + self.theTitle.setText("") + self.theHandle = tHandle + if tHandle is None: + self.closeButton.setVisible(False) + self.refreshButton.setVisible(False) + return False + + if self.mainConf.showFullPath: + tTitle = [] + tTree = self.theProject.projTree.getItemPath(tHandle) + for aHandle in reversed(tTree): + nwItem = self.theProject.projTree[aHandle] + if nwItem is not None: + tTitle.append(nwItem.itemName) + sSep = " %s " % nwUnicode.U_RSAQUO + self.theTitle.setText(sSep.join(tTitle)) + else: + nwItem = self.theProject.projTree[tHandle] + if nwItem is None: + return False + self.theTitle.setText(nwItem.itemName) + + self.closeButton.setVisible(True) + self.refreshButton.setVisible(True) + + return True + + ## + # Slots + ## + + def _closeDocument(self): + """Trigger the close editor/viewer on the main window. + """ + self.theParent.theParent.closeDocViewer() + return + + def _refreshDocument(self): + """Reload the content of the document. + """ + self.theParent.reloadText() + return + + ## + # Events + ## + + def mousePressEvent(self, theEvent): + """Capture a click on the title and ensure that the item is + selected in the project tree. + """ + self.theParent.setSelectedHandle(self.theHandle) + return + +# END Class GuiDocViewHeader + +# =============================================================================================== # +# The Embedded Document Footer +# Only used by DocViewer, and is at a fixed position in the QTextBrowser's viewport +# =============================================================================================== # + +class GuiDocViewFooter(QWidget): + + def __init__(self, theParent): + QWidget.__init__(self, theParent) + + logger.debug("Initialising GuiDocViewFooter ...") + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theTheme = theParent.theTheme + self.optState = theParent.theProject.optState + self.viewMeta = theParent.theParent.viewMeta + self.theHandle = None + + # Make a QPalette that matches the Syntax Theme + self.thePalette = QPalette() + self.thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) + self.thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) + + fPx = int(0.9*self.theTheme.fontPixelSize) + hSp = self.mainConf.pxInt(6) + + # Main Widget Settings + self.setContentsMargins(0, 0, 0, 0) + self.setAutoFillBackground(True) + self.setPalette(self.thePalette) + + buttonStyle = ( + "QToolButton {{border: none; background: transparent;}} " + "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" + ).format(*self.theTheme.colText) + + # Show/Hide Details + self.showHide = QToolButton(self) + self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.showHide.setStyleSheet(buttonStyle) + self.showHide.setIcon(self.theTheme.getIcon("reference")) + self.showHide.setIconSize(QSize(fPx, fPx)) + self.showHide.setFixedSize(QSize(fPx, fPx)) + self.showHide.clicked.connect(self._doShowHide) + + # Sticky Button + stickyOn = self.theTheme.getPixmap("sticky-on", (fPx, fPx)) + stickyOff = self.theTheme.getPixmap("sticky-off", (fPx, fPx)) + stickyIcon = QIcon() + stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On) + stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off) + self.stickyRefs = QToolButton(self) + self.stickyRefs.setCheckable(True) + self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.stickyRefs.setStyleSheet(buttonStyle) + self.stickyRefs.setIcon(stickyIcon) + self.stickyRefs.setIconSize(QSize(fPx, fPx)) + self.stickyRefs.setFixedSize(QSize(fPx, fPx)) + self.stickyRefs.toggled.connect(self._doToggleSticky) + + # Labels + self.lblRefs = QLabel("References") + self.lblRefs.setBuddy(self.showHide) + self.lblRefs.setIndent(0) + self.lblRefs.setMargin(0) + self.lblRefs.setContentsMargins(0, 0, 0, 0) + self.lblRefs.setAutoFillBackground(True) + self.lblRefs.setFixedHeight(fPx) + self.lblRefs.setAlignment(Qt.AlignLeft | Qt.AlignTop) + self.lblRefs.setPalette(self.thePalette) + + self.lblSticky = QLabel("Sticky") + self.lblSticky.setBuddy(self.stickyRefs) + self.lblSticky.setIndent(0) + self.lblSticky.setMargin(0) + self.lblSticky.setContentsMargins(0, 0, 0, 0) + self.lblSticky.setAutoFillBackground(True) + self.lblSticky.setFixedHeight(fPx) + self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop) + self.lblSticky.setPalette(self.thePalette) + + lblFont = self.font() + lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) + self.lblRefs.setFont(lblFont) + self.lblSticky.setFont(lblFont) + + # Assemble Layout + self.outerBox = QHBoxLayout() + self.outerBox.setSpacing(hSp) + self.outerBox.addWidget(self.showHide, 0) + self.outerBox.addWidget(self.lblRefs, 0) + self.outerBox.addWidget(self.stickyRefs, 0) + self.outerBox.addWidget(self.lblSticky, 0) + self.outerBox.addStretch(1) + self.setLayout(self.outerBox) + + logger.debug("GuiDocViewFooter initialisation complete") + + return + + ## + # Slots + ## + + def _doShowHide(self): + """Toggle the expand/collapse of the panel. + """ + isVisible = self.viewMeta.isVisible() + self.viewMeta.setVisible(not isVisible) + return + + def _doToggleSticky(self, theState): + """Toggle the sticky flag for the reference panel. + """ + logger.verbose("Reference sticky is %s" % str(theState)) + self.theParent.stickyRef = theState + if not theState and self.theParent.theHandle is not None: + self.viewMeta.refreshReferences(self.theParent.theHandle) + return + +# END Class GuiDocViewFooter + +# =============================================================================================== # +# The Document Back-Reference Panel +# Placed in a separate QSplitter position in the main GUI window +# =============================================================================================== # + +class GuiDocViewDetails(QScrollArea): + + def __init__(self, theParent): + QScrollArea.__init__(self, theParent) + + logger.debug("Initialising GuiDocViewDetails ...") + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theParent.theProject + self.theTheme = theParent.theTheme + self.currHandle = None + + self.refList = QLabel("") + self.refList.setWordWrap(True) + self.refList.setAlignment(Qt.AlignTop) + self.refList.setScaledContents(True) + self.refList.linkActivated.connect(self._linkClicked) + + hCol = self.palette().highlight().color() + self.linkStyle = "style='color: rgb({0},{1},{2})'".format( + *self.theTheme.colLink + ) + + # Assemble + self.outerWidget = QWidget() + self.outerBox = QHBoxLayout() + self.outerBox.addWidget(self.refList, 1) + + self.outerWidget.setLayout(self.outerBox) + self.setWidget(self.outerWidget) + + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.setWidgetResizable(True) + self.setMinimumHeight(self.mainConf.pxInt(50)) + + logger.debug("GuiDocViewDetails initialisation complete") + + return + + def refreshReferences(self, tHandle): + """Update the current list of document references from the + project index. + """ + self.currHandle = tHandle + if self.theParent.docViewer.stickyRef: + return + + theRefs = self.theParent.theIndex.getBackReferenceList(tHandle) + theList = [] + for tHandle in theRefs: + tItem = self.theProject.projTree[tHandle] + if tItem is not None: + theList.append("%s" % ( + tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName + )) + + self.refList.setText(", ".join(theList)) + + return + + ## + # Internal Functions + ## + + def _linkClicked(self, theLink): + """Capture the link-click and forward it to the document viewer + class for handling. + """ + logger.verbose("Clicked link: '%s'" % theLink) + if len(theLink) == 27: + tHandle = theLink[6:19] + self.theParent.viewDocument(tHandle, theLink) + return + +# END Class GuiDocViewDetails From fcd887a67e294854f2b425ed026ab51666c0a498 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 17:37:17 +0200 Subject: [PATCH 4/8] Updated the look of the doc view footer --- nw/assets/icons/fallback/reference-dark.svg | 12 +++---- nw/assets/icons/fallback/reference.svg | 18 +++++------ nw/assets/icons/typicons_colour_dark/at.svg | 31 +++++++++++++++++++ .../icons/typicons_colour_dark/icons.conf | 2 +- nw/assets/icons/typicons_colour_light/at.svg | 31 +++++++++++++++++++ .../icons/typicons_colour_light/icons.conf | 2 +- nw/assets/icons/typicons_grey_dark/at.svg | 31 +++++++++++++++++++ nw/assets/icons/typicons_grey_dark/icons.conf | 2 +- nw/assets/icons/typicons_grey_light/at.svg | 31 +++++++++++++++++++ .../icons/typicons_grey_light/icons.conf | 2 +- nw/gui/docviewer.py | 11 +++---- 11 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 nw/assets/icons/typicons_colour_dark/at.svg create mode 100644 nw/assets/icons/typicons_colour_light/at.svg create mode 100644 nw/assets/icons/typicons_grey_dark/at.svg create mode 100644 nw/assets/icons/typicons_grey_light/at.svg diff --git a/nw/assets/icons/fallback/reference-dark.svg b/nw/assets/icons/fallback/reference-dark.svg index e57b19b0..4d98eb91 100644 --- a/nw/assets/icons/fallback/reference-dark.svg +++ b/nw/assets/icons/fallback/reference-dark.svg @@ -5,13 +5,13 @@ 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" - id="svg2608" + id="svg1321" viewBox="0 0 24 24" height="24" width="24" version="1.2"> + id="metadata1327"> @@ -23,9 +23,9 @@ + id="defs1325" /> + style="stroke-width:1.25;fill:#aeaeae;fill-opacity:1" + id="path1319" + d="M 12,2 C 6.48625,2 2,6.48625 2,12 c 0,5.51375 4.48625,10 10,10 2.02,0 3.965,-0.59875 5.62375,-1.73 0.57,-0.39 0.7175,-1.1675 0.32875,-1.7375 -0.38875,-0.57125 -1.165,-0.715 -1.7375,-0.32875 C 14.9725,19.0525 13.515,19.5 12,19.5 7.86375,19.5 4.5,16.13625 4.5,12 4.5,7.86375 7.86375,4.5 12,4.5 c 4.13625,0 7.5,3.36375 7.5,7.5 v 0.625 c 0,0.69 -0.56,1.25 -1.25,1.25 -0.69,0 -1.25,-0.56 -1.25,-1.25 v -3.75 c 0,-0.69125 -0.55875,-1.25 -1.25,-1.25 -0.55125,0 -1.00625,0.3625 -1.1725,0.86 C 13.8525,7.95125 12.96625,7.625 12,7.625 9.5875,7.625 7.625,9.5875 7.625,12 c 0,2.4125 1.9625,4.375 4.375,4.375 1.30625,0 2.46875,-0.5875 3.27,-1.49875 0.685,0.90375 1.76,1.49875 2.98,1.49875 2.0675,0 3.75,-1.6825 3.75,-3.75 V 12 C 22,6.48625 17.51375,2 12,2 Z m 0,11.875 c -1.03375,0 -1.875,-0.84125 -1.875,-1.875 0,-1.03375 0.84125,-1.875 1.875,-1.875 1.03375,0 1.875,0.84125 1.875,1.875 0,1.03375 -0.84125,1.875 -1.875,1.875 z" /> diff --git a/nw/assets/icons/fallback/reference.svg b/nw/assets/icons/fallback/reference.svg index 09202533..b39357c9 100644 --- a/nw/assets/icons/fallback/reference.svg +++ b/nw/assets/icons/fallback/reference.svg @@ -5,13 +5,13 @@ 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" + id="svg1321" viewBox="0 0 24 24" - id="svg2608"> + height="24" + width="24" + version="1.2"> + id="metadata1327"> @@ -23,9 +23,9 @@ + id="defs1325" /> + style="stroke-width:1.25;fill:#000000;fill-opacity:0.78039217" + id="path1319" + d="M 12,2 C 6.48625,2 2,6.48625 2,12 c 0,5.51375 4.48625,10 10,10 2.02,0 3.965,-0.59875 5.62375,-1.73 0.57,-0.39 0.7175,-1.1675 0.32875,-1.7375 -0.38875,-0.57125 -1.165,-0.715 -1.7375,-0.32875 C 14.9725,19.0525 13.515,19.5 12,19.5 7.86375,19.5 4.5,16.13625 4.5,12 4.5,7.86375 7.86375,4.5 12,4.5 c 4.13625,0 7.5,3.36375 7.5,7.5 v 0.625 c 0,0.69 -0.56,1.25 -1.25,1.25 -0.69,0 -1.25,-0.56 -1.25,-1.25 v -3.75 c 0,-0.69125 -0.55875,-1.25 -1.25,-1.25 -0.55125,0 -1.00625,0.3625 -1.1725,0.86 C 13.8525,7.95125 12.96625,7.625 12,7.625 9.5875,7.625 7.625,9.5875 7.625,12 c 0,2.4125 1.9625,4.375 4.375,4.375 1.30625,0 2.46875,-0.5875 3.27,-1.49875 0.685,0.90375 1.76,1.49875 2.98,1.49875 2.0675,0 3.75,-1.6825 3.75,-3.75 V 12 C 22,6.48625 17.51375,2 12,2 Z m 0,11.875 c -1.03375,0 -1.875,-0.84125 -1.875,-1.875 0,-1.03375 0.84125,-1.875 1.875,-1.875 1.03375,0 1.875,0.84125 1.875,1.875 0,1.03375 -0.84125,1.875 -1.875,1.875 z" /> diff --git a/nw/assets/icons/typicons_colour_dark/at.svg b/nw/assets/icons/typicons_colour_dark/at.svg new file mode 100644 index 00000000..60bd3acc --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/at.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 3f8b547f..33aeef93 100644 --- a/nw/assets/icons/typicons_colour_dark/icons.conf +++ b/nw/assets/icons/typicons_colour_dark/icons.conf @@ -49,6 +49,6 @@ edit = pencil.svg check = tick.svg cross = times.svg hash = hash.svg -reference = bookmark.svg +reference = at.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/assets/icons/typicons_colour_light/at.svg b/nw/assets/icons/typicons_colour_light/at.svg new file mode 100644 index 00000000..7f34cade --- /dev/null +++ b/nw/assets/icons/typicons_colour_light/at.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 b080af1d..0ba6dd6b 100644 --- a/nw/assets/icons/typicons_colour_light/icons.conf +++ b/nw/assets/icons/typicons_colour_light/icons.conf @@ -49,6 +49,6 @@ edit = pencil.svg check = tick.svg cross = times.svg hash = hash.svg -reference = bookmark.svg +reference = at.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/assets/icons/typicons_grey_dark/at.svg b/nw/assets/icons/typicons_grey_dark/at.svg new file mode 100644 index 00000000..4d98eb91 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/at.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 0a68c931..301c74ac 100644 --- a/nw/assets/icons/typicons_grey_dark/icons.conf +++ b/nw/assets/icons/typicons_grey_dark/icons.conf @@ -49,6 +49,6 @@ edit = pencil.svg check = tick.svg cross = times.svg hash = hash.svg -reference = bookmark.svg +reference = at.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/assets/icons/typicons_grey_light/at.svg b/nw/assets/icons/typicons_grey_light/at.svg new file mode 100644 index 00000000..b39357c9 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/at.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 2c019496..6e98346a 100644 --- a/nw/assets/icons/typicons_grey_light/icons.conf +++ b/nw/assets/icons/typicons_grey_light/icons.conf @@ -49,6 +49,6 @@ edit = pencil.svg check = tick.svg cross = times.svg hash = hash.svg -reference = bookmark.svg +reference = at.svg sticky-on = pin.svg sticky-off = pin-outline.svg diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 74b20fc7..e227487c 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -36,8 +36,7 @@ from PyQt5.QtGui import ( QTextOption, QFont, QPalette, QColor, QTextCursor, QIcon ) from PyQt5.QtWidgets import ( - QTextBrowser, QWidget, QScrollArea, QLabel, QHBoxLayout, QFrame, - QToolButton + QTextBrowser, QWidget, QScrollArea, QLabel, QHBoxLayout, QToolButton ) from nw.core import ToHtml @@ -436,8 +435,6 @@ class GuiDocViewHeader(QWidget): self.theTitle.setAutoFillBackground(True) self.theTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop) self.theTitle.setFixedHeight(fPx) - self.theTitle.setFrameShape(QFrame.NoFrame) - self.theTitle.setLineWidth(0) self.theTitle.setPalette(self.thePalette) lblFont = self.theTitle.font() @@ -571,7 +568,8 @@ class GuiDocViewFooter(QWidget): self.thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) fPx = int(0.9*self.theTheme.fontPixelSize) - hSp = self.mainConf.pxInt(6) + bSp = self.mainConf.pxInt(2) + hSp = self.mainConf.pxInt(8) # Main Widget Settings self.setContentsMargins(0, 0, 0, 0) @@ -635,9 +633,10 @@ class GuiDocViewFooter(QWidget): # Assemble Layout self.outerBox = QHBoxLayout() - self.outerBox.setSpacing(hSp) + self.outerBox.setSpacing(bSp) self.outerBox.addWidget(self.showHide, 0) self.outerBox.addWidget(self.lblRefs, 0) + self.outerBox.addSpacing(hSp) self.outerBox.addWidget(self.stickyRefs, 0) self.outerBox.addWidget(self.lblSticky, 0) self.outerBox.addStretch(1) From 2b878777d4cf5f3198612763b55986ceb281f16a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 17:49:39 +0200 Subject: [PATCH 5/8] Moved the header bar for the editor into the doceditor file --- nw/gui/__init__.py | 3 +- nw/gui/docbars.py | 185 -------------------------------------------- nw/gui/doceditor.py | 176 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 164 insertions(+), 200 deletions(-) diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py index 8800834d..820a2a48 100644 --- a/nw/gui/__init__.py +++ b/nw/gui/__init__.py @@ -2,7 +2,7 @@ from nw.gui.about import GuiAbout from nw.gui.build import GuiBuildNovel -from nw.gui.docbars import GuiDocTitleBar, GuiSearchBar +from nw.gui.docbars import GuiSearchBar from nw.gui.doceditor import GuiDocEditor from nw.gui.docmerge import GuiDocMerge from nw.gui.docsplit import GuiDocSplit @@ -23,7 +23,6 @@ from nw.gui.theme import GuiIcons, GuiTheme __all__ = [ "GuiAbout", "GuiBuildNovel", - "GuiDocTitleBar", "GuiSearchBar", "GuiDocEditor", "GuiDocMerge", diff --git a/nw/gui/docbars.py b/nw/gui/docbars.py index fcbc1869..079f989a 100644 --- a/nw/gui/docbars.py +++ b/nw/gui/docbars.py @@ -7,7 +7,6 @@ File History: Created: 2019-09-29 [0.2.1] GuiSearchBar - Created: 2020-04-25 [0.4.5] GuiDocTitleBar This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen @@ -167,187 +166,3 @@ class GuiSearchBar(QWidget): return True # END Class GuiSearchBar - -class GuiDocTitleBar(QWidget): - - def __init__(self, theParent, isEditor): - QWidget.__init__(self, theParent) - - logger.debug("Initialising GuiDocTitleBar ...") - - self.mainConf = nw.CONFIG - self.theParent = theParent - self.theProject = theParent.theProject - self.theTheme = theParent.theTheme - self.theHandle = None - self.isEditor = isEditor - - # Make a QPalette that matches the Syntax Theme - self.thePalette = QPalette() - self.thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) - self.thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) - - 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) - - # Title Label - self.theTitle = QLabel() - self.theTitle.setText("") - self.theTitle.setIndent(0) - self.theTitle.setMargin(0) - self.theTitle.setContentsMargins(0, 0, 0, 0) - self.theTitle.setAutoFillBackground(True) - self.theTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop) - self.theTitle.setFixedHeight(fPx) - self.theTitle.setFrameShape(QFrame.NoFrame) - self.theTitle.setLineWidth(0) - self.theTitle.setPalette(self.thePalette) - - lblFont = self.theTitle.font() - lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) - self.theTitle.setFont(lblFont) - - buttonStyle = ( - "QToolButton {{border: none; background: transparent;}} " - "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" - ).format(*self.theTheme.colText) - - # Buttons - self.closeButton = QToolButton(self) - self.closeButton.setIcon(self.theTheme.getIcon("close")) - self.closeButton.setContentsMargins(0, 0, 0, 0) - self.closeButton.setIconSize(QSize(fPx, fPx)) - self.closeButton.setFixedSize(fPx, fPx) - self.closeButton.setStyleSheet(buttonStyle) - self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.closeButton.setVisible(False) - self.closeButton.clicked.connect(self._closeDocument) - - if self.isEditor: - self.minmaxButton = QToolButton(self) - self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) - self.minmaxButton.setContentsMargins(0, 0, 0, 0) - self.minmaxButton.setIconSize(QSize(fPx, fPx)) - self.minmaxButton.setFixedSize(fPx, fPx) - self.minmaxButton.setStyleSheet(buttonStyle) - self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.minmaxButton.setVisible(False) - self.minmaxButton.clicked.connect(self._minmaxDocument) - else: - self.refreshButton = QToolButton(self) - self.refreshButton.setIcon(self.theTheme.getIcon("refresh")) - self.refreshButton.setContentsMargins(0, 0, 0, 0) - self.refreshButton.setIconSize(QSize(fPx, fPx)) - self.refreshButton.setFixedSize(fPx, fPx) - self.refreshButton.setStyleSheet(buttonStyle) - self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.refreshButton.setVisible(False) - self.refreshButton.clicked.connect(self._refreshDocument) - - # Assemble Layout - self.outerBox = QHBoxLayout() - self.outerBox.setSpacing(hSp) - self.outerBox.addWidget(self.theTitle, 1) - if self.isEditor: - self.outerBox.addWidget(self.minmaxButton, 0) - else: - self.outerBox.addWidget(self.refreshButton, 0) - self.outerBox.addWidget(self.closeButton, 0) - self.setLayout(self.outerBox) - - logger.debug("GuiDocTitleBar initialisation complete") - - return - - ## - # Setters - ## - - def setTitleFromHandle(self, tHandle): - """Sets the document title from the handle, or alternatively, - set the whole document path. - """ - self.theTitle.setText("") - self.theHandle = tHandle - if tHandle is None: - self.closeButton.setVisible(False) - if self.isEditor: - self.minmaxButton.setVisible(False) - else: - self.refreshButton.setVisible(False) - return False - - if self.mainConf.showFullPath: - tTitle = [] - tTree = self.theProject.projTree.getItemPath(tHandle) - for aHandle in reversed(tTree): - nwItem = self.theProject.projTree[aHandle] - if nwItem is not None: - tTitle.append(nwItem.itemName) - sSep = " %s " % nwUnicode.U_RSAQUO - self.theTitle.setText(sSep.join(tTitle)) - else: - nwItem = self.theProject.projTree[tHandle] - if nwItem is None: - return False - self.theTitle.setText(nwItem.itemName) - - self.closeButton.setVisible(True) - if self.isEditor: - self.minmaxButton.setVisible(True) - else: - self.refreshButton.setVisible(True) - - return True - - ## - # Slots - ## - - def _closeDocument(self): - """Trigger the close editor/viewer on the main window. - """ - if self.isEditor: - self.theParent.theParent.closeDocEditor() - else: - self.theParent.theParent.closeDocViewer() - return - - def _minmaxDocument(self): - """Switch on or off zen mode. - """ - self.theParent.theParent.toggleZenMode() - if self.theParent.theParent.isZenMode: - self.minmaxButton.setIcon(self.theTheme.getIcon("minimise")) - self.setContentsMargins(self.buttonSize, 0, 0, 0) - self.closeButton.setVisible(False) - else: - self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) - self.setContentsMargins(2*self.buttonSize, 0, 0, 0) - self.closeButton.setVisible(True) - return - - def _refreshDocument(self): - """Reload the content of the document. - """ - self.theParent.reloadText() - return - - ## - # Events - ## - - def mousePressEvent(self, theEvent): - """Capture a click on the title and ensure that the item is - selected in the project tree. - """ - self.theParent.setSelectedHandle(self.theHandle) - return - -# END Class GuiDocTitleBar diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index fadd6dd5..9089110b 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -8,6 +8,7 @@ File History: Created: 2018-09-29 [0.0.1] GuiDocEditor Created: 2019-04-22 [0.0.1] WordCounter + Created: 2020-04-25 [0.4.5] GuiDocEditHeader This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen @@ -31,18 +32,18 @@ import nw from time import time -from PyQt5.QtCore import Qt, QThread, QTimer, pyqtSlot -from PyQt5.QtWidgets import ( - qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox -) +from PyQt5.QtCore import Qt, QSize, QThread, QTimer, pyqtSlot from PyQt5.QtGui import ( QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette, QTextDocument, QCursor ) +from PyQt5.QtWidgets import ( + qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox, QWidget, QLabel, + QToolButton, QHBoxLayout +) from nw.core import NWDoc from nw.gui.dochighlight import GuiDocHighlighter -from nw.gui.docbars import GuiDocTitleBar from nw.core import NWSpellSimple, countWords from nw.constants import nwUnicode, nwDocAction @@ -85,9 +86,7 @@ class GuiDocEditor(QTextEdit): self.qDocument.contentsChange.connect(self._docChange) # Document Title - self.docTitle = GuiDocTitleBar(self, isEditor=True) - self.docTitle.setGeometry(0, 0, self.docTitle.width(), self.docTitle.height()) - self.setViewportMargins(0, self.docTitle.height(), 0, 0) + self.docHeader = GuiDocEditHeader(self) # Syntax self.hLight = GuiDocHighlighter(self.qDocument, self.theParent) @@ -158,7 +157,7 @@ class GuiDocEditor(QTextEdit): self.hasSelection = False self.setDocumentChanged(False) - self.docTitle.setTitleFromHandle(self.theHandle) + self.docHeader.setTitleFromHandle(self.theHandle) return True @@ -270,7 +269,7 @@ class GuiDocEditor(QTextEdit): self.theHandle = tHandle self.setReadOnly(False) - self.docTitle.setTitleFromHandle(self.theHandle) + self.docHeader.setTitleFromHandle(self.theHandle) self.updateDocMargins() self.hLight.spellCheck = spTemp qApp.restoreOverrideCursor() @@ -335,9 +334,9 @@ class GuiDocEditor(QTextEdit): tB = self.frameWidth() tW = self.width() - 2*tB - sW - tH = self.docTitle.height() + tH = self.docHeader.height() tT = cM - tH - self.docTitle.setGeometry(tB, tB, tW, tH) + self.docHeader.setGeometry(tB, tB, tW, tH) self.setViewportMargins(0, tH, 0, 0) docFormat = self.qDocument.rootFrame().frameFormat() @@ -368,7 +367,7 @@ class GuiDocEditor(QTextEdit): title bar needs updating, """ if tHandle == self.theHandle: - self.docTitle.setTitleFromHandle(self.theHandle) + self.docHeader.setTitleFromHandle(self.theHandle) self.updateDocMargins() return @@ -1105,3 +1104,154 @@ class WordCounter(QThread): return ## END Class WordCounter + +class GuiDocEditHeader(QWidget): + + def __init__(self, theParent): + QWidget.__init__(self, theParent) + + logger.debug("Initialising GuiDocEditHeader ...") + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theParent.theProject + self.theTheme = theParent.theTheme + self.theHandle = None + + # Make a QPalette that matches the Syntax Theme + self.thePalette = QPalette() + self.thePalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) + self.thePalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) + + 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) + + # Title Label + self.theTitle = QLabel() + self.theTitle.setText("") + self.theTitle.setIndent(0) + self.theTitle.setMargin(0) + self.theTitle.setContentsMargins(0, 0, 0, 0) + self.theTitle.setAutoFillBackground(True) + self.theTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop) + self.theTitle.setFixedHeight(fPx) + self.theTitle.setPalette(self.thePalette) + + lblFont = self.theTitle.font() + lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) + self.theTitle.setFont(lblFont) + + buttonStyle = ( + "QToolButton {{border: none; background: transparent;}} " + "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}" + ).format(*self.theTheme.colText) + + # Buttons + self.minmaxButton = QToolButton(self) + self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) + self.minmaxButton.setContentsMargins(0, 0, 0, 0) + self.minmaxButton.setIconSize(QSize(fPx, fPx)) + self.minmaxButton.setFixedSize(fPx, fPx) + self.minmaxButton.setStyleSheet(buttonStyle) + self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.minmaxButton.setVisible(False) + self.minmaxButton.clicked.connect(self._minmaxDocument) + + self.closeButton = QToolButton(self) + self.closeButton.setIcon(self.theTheme.getIcon("close")) + self.closeButton.setContentsMargins(0, 0, 0, 0) + self.closeButton.setIconSize(QSize(fPx, fPx)) + self.closeButton.setFixedSize(fPx, fPx) + self.closeButton.setStyleSheet(buttonStyle) + self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.closeButton.setVisible(False) + self.closeButton.clicked.connect(self._closeDocument) + + # Assemble Layout + self.outerBox = QHBoxLayout() + self.outerBox.setSpacing(hSp) + self.outerBox.addWidget(self.theTitle, 1) + self.outerBox.addWidget(self.minmaxButton, 0) + self.outerBox.addWidget(self.closeButton, 0) + self.setLayout(self.outerBox) + + logger.debug("GuiDocEditHeader initialisation complete") + + return + + ## + # Setters + ## + + def setTitleFromHandle(self, tHandle): + """Sets the document title from the handle, or alternatively, + set the whole document path. + """ + self.theTitle.setText("") + self.theHandle = tHandle + if tHandle is None: + self.closeButton.setVisible(False) + self.minmaxButton.setVisible(False) + + if self.mainConf.showFullPath: + tTitle = [] + tTree = self.theProject.projTree.getItemPath(tHandle) + for aHandle in reversed(tTree): + nwItem = self.theProject.projTree[aHandle] + if nwItem is not None: + tTitle.append(nwItem.itemName) + sSep = " %s " % nwUnicode.U_RSAQUO + self.theTitle.setText(sSep.join(tTitle)) + else: + nwItem = self.theProject.projTree[tHandle] + if nwItem is None: + return False + self.theTitle.setText(nwItem.itemName) + + self.closeButton.setVisible(True) + self.minmaxButton.setVisible(True) + + return True + + ## + # Slots + ## + + def _closeDocument(self): + """Trigger the close editor/viewer on the main window. + """ + self.theParent.theParent.closeDocEditor() + return + + def _minmaxDocument(self): + """Switch on or off zen mode. + """ + self.theParent.theParent.toggleZenMode() + if self.theParent.theParent.isZenMode: + self.minmaxButton.setIcon(self.theTheme.getIcon("minimise")) + self.setContentsMargins(self.buttonSize, 0, 0, 0) + self.closeButton.setVisible(False) + else: + self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) + self.setContentsMargins(2*self.buttonSize, 0, 0, 0) + self.closeButton.setVisible(True) + return + + ## + # Events + ## + + def mousePressEvent(self, theEvent): + """Capture a click on the title and ensure that the item is + selected in the project tree. + """ + self.theParent.setSelectedHandle(self.theHandle) + return + +# END Class GuiDocEditHeader From c3e6007f7660c272ac24b49d25783688850319c5 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 17:54:43 +0200 Subject: [PATCH 6/8] Some cleanup in the doc edit header --- nw/gui/doceditor.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 9089110b..5313207c 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -1107,15 +1107,16 @@ class WordCounter(QThread): class GuiDocEditHeader(QWidget): - def __init__(self, theParent): - QWidget.__init__(self, theParent) + def __init__(self, docEditor): + QWidget.__init__(self, docEditor) logger.debug("Initialising GuiDocEditHeader ...") self.mainConf = nw.CONFIG - self.theParent = theParent - self.theProject = theParent.theProject - self.theTheme = theParent.theTheme + self.docEditor = docEditor + self.theParent = docEditor.theParent + self.theProject = docEditor.theProject + self.theTheme = docEditor.theTheme self.theHandle = None # Make a QPalette that matches the Syntax Theme @@ -1193,11 +1194,12 @@ class GuiDocEditHeader(QWidget): """Sets the document title from the handle, or alternatively, set the whole document path. """ - self.theTitle.setText("") self.theHandle = tHandle if tHandle is None: + self.theTitle.setText("") self.closeButton.setVisible(False) self.minmaxButton.setVisible(False) + return True if self.mainConf.showFullPath: tTitle = [] @@ -1226,14 +1228,16 @@ class GuiDocEditHeader(QWidget): def _closeDocument(self): """Trigger the close editor/viewer on the main window. """ - self.theParent.theParent.closeDocEditor() + self.theParent.closeDocEditor() + self.closeButton.setVisible(False) + self.minmaxButton.setVisible(False) return def _minmaxDocument(self): """Switch on or off zen mode. """ - self.theParent.theParent.toggleZenMode() - if self.theParent.theParent.isZenMode: + self.theParent.toggleZenMode() + if self.theParent.isZenMode: self.minmaxButton.setIcon(self.theTheme.getIcon("minimise")) self.setContentsMargins(self.buttonSize, 0, 0, 0) self.closeButton.setVisible(False) @@ -1251,7 +1255,7 @@ class GuiDocEditHeader(QWidget): """Capture a click on the title and ensure that the item is selected in the project tree. """ - self.theParent.setSelectedHandle(self.theHandle) + self.theParent.treeView.setSelectedHandle(self.theHandle) return # END Class GuiDocEditHeader From 9671db0b42714b2193a06a17490f402d67b6bd28 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 17:57:58 +0200 Subject: [PATCH 7/8] Some cleanup in the doc view header --- nw/gui/doceditor.py | 3 --- nw/gui/docviewer.py | 24 +++++++++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 5313207c..bd5da37f 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -133,9 +133,6 @@ class GuiDocEditor(QTextEdit): logger.debug("GuiDocEditor initialisation complete") - # Connect Functions - self.setSelectedHandle = self.theParent.treeView.setSelectedHandle - return def clearEditor(self): diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index e227487c..c3bfc51b 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -78,9 +78,6 @@ class GuiDocViewer(QTextBrowser): logger.debug("GuiDocViewer initialisation complete") - # Connect Functions - self.setSelectedHandle = self.theParent.treeView.setSelectedHandle - return def clearViewer(self): @@ -401,15 +398,16 @@ class GuiDocViewer(QTextBrowser): class GuiDocViewHeader(QWidget): - def __init__(self, theParent): - QWidget.__init__(self, theParent) + def __init__(self, docViewer): + QWidget.__init__(self, docViewer) logger.debug("Initialising GuiDocViewHeader ...") self.mainConf = nw.CONFIG - self.theParent = theParent - self.theProject = theParent.theProject - self.theTheme = theParent.theTheme + self.docViewer = docViewer + self.theParent = docViewer.theParent + self.theProject = docViewer.theProject + self.theTheme = docViewer.theTheme self.theHandle = None # Make a QPalette that matches the Syntax Theme @@ -487,12 +485,12 @@ class GuiDocViewHeader(QWidget): """Sets the document title from the handle, or alternatively, set the whole document path. """ - self.theTitle.setText("") self.theHandle = tHandle if tHandle is None: + self.theTitle.setText("") self.closeButton.setVisible(False) self.refreshButton.setVisible(False) - return False + return True if self.mainConf.showFullPath: tTitle = [] @@ -521,13 +519,13 @@ class GuiDocViewHeader(QWidget): def _closeDocument(self): """Trigger the close editor/viewer on the main window. """ - self.theParent.theParent.closeDocViewer() + self.theParent.closeDocViewer() return def _refreshDocument(self): """Reload the content of the document. """ - self.theParent.reloadText() + self.docViewer.reloadText() return ## @@ -538,7 +536,7 @@ class GuiDocViewHeader(QWidget): """Capture a click on the title and ensure that the item is selected in the project tree. """ - self.theParent.setSelectedHandle(self.theHandle) + self.theParent.treeView.setSelectedHandle(self.theHandle) return # END Class GuiDocViewHeader From 20ff46b8d22346b0e75f779c009446c5087934ac Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 11 Jun 2020 18:02:23 +0200 Subject: [PATCH 8/8] Fixed issue with following tag link when doc viewer is closed. --- nw/gui/docviewer.py | 5 ++++- nw/gui/outlinedetails.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index c3bfc51b..a2884dab 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -188,7 +188,10 @@ class GuiDocViewer(QTextBrowser): ) % theTag, nwAlert.ERROR) return else: - self.loadText(tHandle) + # 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. + self.theParent.viewDocument(tHandle) self.navigateTo("#head_%s:%s" % (tHandle, sTitle)) return True diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py index 9a010559..5e197449 100644 --- a/nw/gui/outlinedetails.py +++ b/nw/gui/outlinedetails.py @@ -277,7 +277,6 @@ class GuiOutlineDetails(QScrollArea): theBits = theLink.split("=") if len(theBits) == 2: self.theParent.docViewer.loadFromTag(theBits[1]) - self.theParent.tabWidget.setCurrentWidget(self.theParent.splitView) return ##