diff --git a/nw/core/options.py b/nw/core/options.py index 12593c0c..abab6a65 100644 --- a/nw/core/options.py +++ b/nw/core/options.py @@ -77,7 +77,7 @@ class OptionState(): "headerOrder", "columnWidth", "columnHidden", - ]) + ]), } return diff --git a/nw/core/project.py b/nw/core/project.py index 45480820..caa81b09 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -579,6 +579,7 @@ class NWProject(): def closeProject(self): """Close the current project and clear all meta data. """ + self.optState.saveSettings() self.projTree.writeToCFiles() self._appendSessionStats() self._clearLockFile() diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py index 312744d6..4073dcc6 100644 --- a/nw/gui/__init__.py +++ b/nw/gui/__init__.py @@ -2,8 +2,7 @@ from nw.gui.about import GuiAbout from nw.gui.build import GuiBuildNovel -from nw.gui.docbars import GuiDocTitleBar, GuiSearchBar -from nw.gui.docdetails import GuiDocViewDetails +from nw.gui.docbars import GuiDocTitleBar, GuiSearchBar, GuiDocViewDetails from nw.gui.doceditor import GuiDocEditor from nw.gui.docmerge import GuiDocMerge from nw.gui.docsplit import GuiDocSplit diff --git a/nw/gui/docbars.py b/nw/gui/docbars.py index 15733d27..9eeeee3f 100644 --- a/nw/gui/docbars.py +++ b/nw/gui/docbars.py @@ -30,10 +30,10 @@ import logging import nw from PyQt5.QtCore import Qt, QSize -from PyQt5.QtGui import QPalette, QColor +from PyQt5.QtGui import QPalette, QColor, QIcon from PyQt5.QtWidgets import ( qApp, QWidget, QFrame, QGridLayout, QLabel, QLineEdit, QPushButton, - QHBoxLayout, QToolButton + QHBoxLayout, QToolButton, QScrollArea ) from nw.constants import nwDocAction, nwUnicode @@ -170,14 +170,14 @@ class GuiSearchBar(QWidget): class GuiDocTitleBar(QWidget): - def __init__(self, theParent, theProject, isEditor): + def __init__(self, theParent, isEditor): QWidget.__init__(self, theParent) logger.debug("Initialising GuiDocTitleBar ...") self.mainConf = nw.CONFIG self.theParent = theParent - self.theProject = theProject + self.theProject = theParent.theProject self.theTheme = theParent.theTheme self.theHandle = None self.isEditor = isEditor @@ -354,16 +354,17 @@ class GuiDocTitleBar(QWidget): class GuiDocViewFooter(QWidget): - def __init__(self, theParent, theProject): + def __init__(self, theParent): QWidget.__init__(self, theParent) logger.debug("Initialising GuiDocViewFooter ...") - self.mainConf = nw.CONFIG - self.theParent = theParent - self.theProject = theProject - self.theTheme = theParent.theTheme - self.theHandle = None + 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() @@ -378,32 +379,69 @@ class GuiDocViewFooter(QWidget): 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.showHide.setStyleSheet("QToolButton { border: none; background: transparent; }") + self.showHide = QToolButton(self) self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.showHide.setStyleSheet(buttonStyle) self.showHide.setIcon(self.theTheme.getIcon("reference")) - self.showHide.setFixedSize(QSize(fPx, fPx)) self.showHide.setIconSize(QSize(fPx, fPx)) + self.showHide.setFixedSize(QSize(fPx, fPx)) self.showHide.clicked.connect(self._doShowHide) - # Title Label - self.theTitle = QLabel("References") - self.theTitle.setIndent(0) - self.theTitle.setMargin(0) - self.theTitle.setContentsMargins(0, 0, 0, 0) - self.theTitle.setAlignment(Qt.AlignLeft | Qt.AlignTop) - self.theTitle.setFixedHeight(fPx) + # 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) - lblFont = self.theTitle.font() + # 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.theTitle.setFont(lblFont) + 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.theTitle, 1) + 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") @@ -417,8 +455,94 @@ class GuiDocViewFooter(QWidget): def _doShowHide(self): """Toggle the expand/collapse of the panel. """ - isVisible = self.theParent.theParent.viewMeta.isVisible() - self.theParent.theParent.viewMeta.setVisible(not isVisible) + 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 DocViewDetails ...") + 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("DocViewDetails 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/doceditor.py b/nw/gui/doceditor.py index 81c37e04..d3b6eb93 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -85,7 +85,7 @@ class GuiDocEditor(QTextEdit): self.qDocument.contentsChange.connect(self._docChange) # Document Title - self.docTitle = GuiDocTitleBar(self, self.theProject, isEditor=True) + 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) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index f6e02d57..0648111b 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -58,8 +58,8 @@ class GuiDocViewer(QTextBrowser): self.initViewer() # Document Title and Footer - self.docTitle = GuiDocTitleBar(self, self.theProject, isEditor=False) - self.docFooter = GuiDocViewFooter(self, self.theProject) + self.docTitle = GuiDocTitleBar(self, isEditor=False) + self.docFooter = GuiDocViewFooter(self) self.stickyRef = False theOpt = QTextOption() diff --git a/nw/guimain.py b/nw/guimain.py index 24deedf7..e34ff448 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -92,8 +92,8 @@ class GuiMain(QMainWindow): self.statusBar = GuiMainStatus(self) self.treeView = GuiProjectTree(self) self.docEditor = GuiDocEditor(self) - self.docViewer = GuiDocViewer(self) self.viewMeta = GuiDocViewDetails(self) + self.docViewer = GuiDocViewer(self) self.searchBar = GuiSearchBar(self) self.treeMeta = GuiItemDetails(self) self.projView = GuiOutline(self) @@ -517,6 +517,7 @@ class GuiMain(QMainWindow): vPos[0] = int(bPos[1]/2) vPos[1] = bPos[1] - vPos[0] self.splitDocs.setSizes(vPos) + self.viewMeta.setVisible(self.mainConf.showRefPanel) self.docViewer.navigateTo(navLink) return True @@ -840,20 +841,23 @@ class GuiMain(QMainWindow): if msgRes != QMessageBox.Yes: return False + logger.info("Exiting %s" % nw.__package__) + if not self.isZenMode: self.mainConf.setMainPanePos(self.splitMain.sizes()) self.mainConf.setDocPanePos(self.splitDocs.sizes()) - self.mainConf.setViewPanePos(self.splitView.sizes()) self.mainConf.setOutlinePanePos(self.splitOutline.sizes()) + if self.viewMeta.isVisible(): + self.mainConf.setViewPanePos(self.splitView.sizes()) - logger.info("Exiting %s" % nw.__package__) - if self.hasProject: - self.closeProject(True) - + self.mainConf.setShowRefPanel(self.viewMeta.isVisible()) self.mainConf.setTreeColWidths(self.treeView.getColumnSizes()) if not self.mainConf.isFullScreen: self.mainConf.setWinSize(self.width(), self.height()) + if self.hasProject: + self.closeProject(True) + self.mainConf.saveConfig() self.reportConfErr()