From 9263348b252b8c0bf5f161f0d330d18a03903dd1 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 26 Apr 2020 20:39:02 +0200 Subject: [PATCH 1/7] Add lock file to git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f772cce6..54fe2f2e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ sample/**/wordlist.txt sample/**/sessionInfo.log sample/**/*.bak sample/**/*.json +sample/**/*.lock # PyTest tests/temp From 3a9de4af38d3194d8b95054d6dcd4eb97de3d9ac Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 26 Apr 2020 20:43:22 +0200 Subject: [PATCH 2/7] Remove dummy text from notixe bar class --- nw/gui/elements/noticebar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nw/gui/elements/noticebar.py b/nw/gui/elements/noticebar.py index c423e93b..c9022262 100644 --- a/nw/gui/elements/noticebar.py +++ b/nw/gui/elements/noticebar.py @@ -49,7 +49,7 @@ class GuiNoticeBar(QFrame): self.mainBox = QHBoxLayout(self) self.mainBox.setContentsMargins(8,2,2,2) - self.noteLabel = QLabel("Hi there!") + self.noteLabel = QLabel("") self.closeButton = QPushButton(self.theTheme.getIcon("close"),"") self.closeButton.clicked.connect(self.hideNote) From 0387ce67b72e41d5fdf47885e9915bbed2d95304 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 26 Apr 2020 21:35:58 +0200 Subject: [PATCH 3/7] Base document title class ready. --- nw/gui/__init__.py | 2 ++ nw/gui/elements/__init__.py | 2 ++ nw/gui/elements/doceditor.py | 2 +- nw/gui/elements/doctitlebar.py | 61 ++++++++++++++++++++++++++++++++++ nw/guimain.py | 10 ++++-- 5 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 nw/gui/elements/doctitlebar.py diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py index 87dec78c..d5f7e67b 100644 --- a/nw/gui/__init__.py +++ b/nw/gui/__init__.py @@ -19,6 +19,7 @@ from nw.gui.dialogs.sessionlog import GuiSessionLogView # GUI Elements from nw.gui.elements.docdetails import GuiDocDetails from nw.gui.elements.doceditor import GuiDocEditor +from nw.gui.elements.doctitlebar import GuiDocTitleBar from nw.gui.elements.doctree import GuiDocTree from nw.gui.elements.docviewer import GuiDocViewer from nw.gui.elements.noticebar import GuiNoticeBar @@ -45,6 +46,7 @@ __all__ = [ "GuiSessionLogView", "GuiDocDetails", "GuiDocEditor", + "GuiDocTitleBar", "GuiDocTree", "GuiDocViewer", "GuiNoticeBar", diff --git a/nw/gui/elements/__init__.py b/nw/gui/elements/__init__.py index 1d885de0..b0938816 100644 --- a/nw/gui/elements/__init__.py +++ b/nw/gui/elements/__init__.py @@ -2,6 +2,7 @@ from nw.gui.elements.docdetails import GuiDocDetails from nw.gui.elements.doceditor import GuiDocEditor +from nw.gui.elements.doctitlebar import GuiDocTitleBar from nw.gui.elements.doctree import GuiDocTree from nw.gui.elements.docviewer import GuiDocViewer from nw.gui.elements.noticebar import GuiNoticeBar @@ -12,6 +13,7 @@ from nw.gui.elements.viewdetails import GuiDocViewDetails __all__ = [ "GuiDocDetails", "GuiDocEditor", + "GuiDocTitleBar", "GuiDocTree", "GuiDocViewer", "GuiNoticeBar", diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index 2ad49a1b..0a63f816 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -131,7 +131,7 @@ class GuiDocEditor(QTextEdit): def clearEditor(self): """Clear the current document and reset all document related - flags and counters. + flags and counters. """ self.nwDocument.clearDocument() diff --git a/nw/gui/elements/doctitlebar.py b/nw/gui/elements/doctitlebar.py new file mode 100644 index 00000000..7addfcdf --- /dev/null +++ b/nw/gui/elements/doctitlebar.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +"""novelWriter GUI Document Title Bar + + novelWriter – GUI Document Title Bar +====================================== + Class holding the document title bar class + + File History: + Created: 2020-04-25 [0.4.5] + + This file is a part of novelWriter + Copyright 2020, Veronica Berglyd Olsen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see https://www.gnu.org/licenses/. +""" + +import logging +import nw + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QPalette, QColor +from PyQt5.QtWidgets import QLabel + +logger = logging.getLogger(__name__) + +class GuiDocTitleBar(QLabel): + + def __init__(self, theParent): + QLabel.__init__(self, theParent) + + logger.debug("Initialising DocTitleBar ...") + + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theTheme = theParent.theTheme + + self.setText("A > B > C") + self.setContentsMargins(8,2,8,2) + self.setAutoFillBackground(True) + self.setAlignment(Qt.AlignCenter) + docPalette = self.palette() + docPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) + docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) + self.setPalette(docPalette) + + logger.debug("DocTitleBar initialisation complete") + + return + +# END Class GuiDocTitleBar diff --git a/nw/guimain.py b/nw/guimain.py index 46a32384..8e561fc7 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -43,7 +43,7 @@ from nw.gui import ( GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiExport, GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails, GuiConfigEditor, GuiProjectEditor, GuiItemEditor, GuiProjectOutline, - GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad + GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad, GuiDocTitleBar ) from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup from nw.tools import countWords @@ -93,6 +93,8 @@ class GuiMain(QMainWindow): self.treeView = GuiDocTree(self, self.theProject) self.projView = GuiProjectOutline(self, self.theProject) self.mainMenu = GuiMainMenu(self, self.theProject) + self.viewTitle = GuiDocTitleBar(self) + self.editTitle = GuiDocTitleBar(self) # Minor Gui Elements self.statusIcons = [] @@ -109,17 +111,21 @@ class GuiMain(QMainWindow): self.editPane = QFrame() self.docEdit = QVBoxLayout() self.docEdit.setContentsMargins(0,0,0,0) + self.docEdit.setSpacing(0) self.docEdit.addWidget(self.searchBar) self.docEdit.addWidget(self.noticeBar) + self.docEdit.addWidget(self.editTitle) self.docEdit.addWidget(self.docEditor) self.editPane.setLayout(self.docEdit) self.viewPane = QFrame() self.docView = QVBoxLayout() self.docView.setContentsMargins(0,0,0,0) + self.docView.setSpacing(0) + self.docView.addWidget(self.viewTitle) self.docView.addWidget(self.docViewer) self.docView.addWidget(self.viewMeta) - self.docView.setStretch(0, 1) + self.docView.setStretch(1, 1) self.viewPane.setLayout(self.docView) self.splitView = QSplitter(Qt.Horizontal) From 7d91a15841571573b74107728d14aa16ec9eda95 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 1 May 2020 17:36:02 +0200 Subject: [PATCH 4/7] The document title bar is no populated on document open --- nw/constants/constants.py | 12 ++++++++++ nw/gui/elements/doctitlebar.py | 43 ++++++++++++++++++++++++++++++---- nw/guimain.py | 11 +++++++-- nw/project/project.py | 25 ++++++++++++++++++++ 4 files changed, 84 insertions(+), 7 deletions(-) diff --git a/nw/constants/constants.py b/nw/constants/constants.py index 643c3a37..3f6192d4 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -234,6 +234,12 @@ class nwUnicode: U_NBSP = "\u00a0" # Non-breaking space U_PARA = "\u2029" # Paragraph separator + ## Arrows + U_UTRI = "\u2bc5" # Up-pointing triangle + U_DTRI = "\u2bc6" # Down-pointing triangle + U_LTRI = "\u2bc7" # Left-pointing triangle + U_RTRI = "\u2bc8" # Right-pointing triangle + # HTML Equivalents ## Quotes @@ -265,4 +271,10 @@ class nwUnicode: ## Other H_NBSP = " " + ## Arrows + H_UTRI = "⯅" + H_DTRI = "⯆" + H_LTRI = "⯇" + H_RTRI = "⯈" + # END Class nwUnicode diff --git a/nw/gui/elements/doctitlebar.py b/nw/gui/elements/doctitlebar.py index 7addfcdf..1d018536 100644 --- a/nw/gui/elements/doctitlebar.py +++ b/nw/gui/elements/doctitlebar.py @@ -32,23 +32,28 @@ from PyQt5.QtCore import Qt from PyQt5.QtGui import QPalette, QColor from PyQt5.QtWidgets import QLabel +from nw.constants import nwUnicode + logger = logging.getLogger(__name__) class GuiDocTitleBar(QLabel): - def __init__(self, theParent): + def __init__(self, theParent, theProject): QLabel.__init__(self, theParent) logger.debug("Initialising DocTitleBar ...") - self.mainConf = nw.CONFIG - self.theParent = theParent - self.theTheme = theParent.theTheme + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theProject + self.theTheme = theParent.theTheme + self.theHandle = None - self.setText("A > B > C") + self.setText("") self.setContentsMargins(8,2,8,2) self.setAutoFillBackground(True) self.setAlignment(Qt.AlignCenter) + self.setWordWrap(True) docPalette = self.palette() docPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) @@ -58,4 +63,32 @@ class GuiDocTitleBar(QLabel): return + def setTitleFromHandle(self, tHandle): + """Sets the document title from the handle, or alternatively, + set the whole document path. + """ + + self.setText("") + self.theHandle = tHandle + if tHandle is None: + return False + + if True: + tTitle = [] + tTree = self.theProject.getItemPath(tHandle) + for aHandle in reversed(tTree): + nwItem = self.theProject.getItem(aHandle) + if nwItem is not None: + tTitle.append(nwItem.itemName) + sSep = " %s " % nwUnicode.U_RTRI + self.setText(sSep.join(tTitle)) + else: + nwItem = self.theProject.getItem(tHandle) + if nwItem is None: + return False + + self.setText(nwItem.itemName) + + return True + # END Class GuiDocTitleBar diff --git a/nw/guimain.py b/nw/guimain.py index 8e561fc7..c9558787 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -93,8 +93,8 @@ class GuiMain(QMainWindow): self.treeView = GuiDocTree(self, self.theProject) self.projView = GuiProjectOutline(self, self.theProject) self.mainMenu = GuiMainMenu(self, self.theProject) - self.viewTitle = GuiDocTitleBar(self) - self.editTitle = GuiDocTitleBar(self) + self.viewTitle = GuiDocTitleBar(self, self.theProject) + self.editTitle = GuiDocTitleBar(self, self.theProject) # Minor Gui Elements self.statusIcons = [] @@ -452,10 +452,13 @@ class GuiMain(QMainWindow): ## def closeDocument(self): + """Close the document and clear the editor and title field. + """ if self.hasProject: if self.docEditor.docChanged: self.saveDocument() self.docEditor.clearEditor() + self.editTitle.setTitleFromHandle(None) return True def openDocument(self, tHandle, tLine=None): @@ -467,6 +470,7 @@ class GuiMain(QMainWindow): if self.docEditor.loadText(tHandle, tLine): self.docEditor.setFocus() self.theProject.setLastEdited(tHandle) + self.editTitle.setTitleFromHandle(tHandle) else: return False return True @@ -477,6 +481,8 @@ class GuiMain(QMainWindow): return True def viewDocument(self, tHandle=None): + """Load a document for viewing in the view panel. + """ if tHandle is None: tHandle = self.treeView.getSelectedHandle() @@ -494,6 +500,7 @@ class GuiMain(QMainWindow): self.tabWidget.setCurrentWidget(self.splitView) if self.docViewer.loadText(tHandle) and not self.viewPane.isVisible(): + self.viewTitle.setTitleFromHandle(tHandle) bPos = self.splitMain.sizes() self.viewPane.setVisible(True) vPos = [0,0] diff --git a/nw/project/project.py b/nw/project/project.py index aa63649a..ef09f671 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -601,6 +601,9 @@ class NWProject(): ## def getItem(self, tHandle): + """Return a project item based on its handle. Returns None if + the handle doesn't exist in the project. + """ if tHandle in self.projTree: return self.projTree[tHandle] logger.error("No tree item with handle %s" % str(tHandle)) @@ -626,6 +629,28 @@ class NWProject(): return tHandle return None + def getItemPath(self, tHandle): + """Iterate upwards in the tree until we find the item with + parent None, the root item, and return the list of handles. + We do this with a for loop with a maximum depth of 200 to make + infinite loops impossible. + """ + tTree = [] + tItem = self.getItem(tHandle) + if tItem is not None: + tTree.append(tHandle) + for i in range(200): + if tItem.parHandle is None: + return tTree + else: + tHandle = tItem.parHandle + tItem = self.getItem(tHandle) + if tItem is None: + return tTree + else: + tTree.append(tHandle) + return tTree + def getProjectItems(self): """This function is called from the tree view when building the tree. Each item in the project is returned in the order saved in From cc1113ea6c64c847c234785dc6caa93db389c0c8 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 1 May 2020 18:11:32 +0200 Subject: [PATCH 5/7] Tie things together to ensure title is always updated, and add some needed comments here and there --- nw/gui/elements/doceditor.py | 2 ++ nw/gui/elements/doctitlebar.py | 15 +++++++++++ nw/gui/elements/doctree.py | 14 ++++++++++ nw/gui/elements/docviewer.py | 8 ++++++ nw/guimain.py | 49 +++++++++++++++++++++++++++++----- 5 files changed, 82 insertions(+), 6 deletions(-) diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index 0a63f816..5888426c 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -150,6 +150,7 @@ class GuiDocEditor(QTextEdit): self.setDocumentChanged(False) self.theParent.noticeBar.hideNote() + self.theParent.updateEditTitle() return True @@ -264,6 +265,7 @@ class GuiDocEditor(QTextEdit): else: self.theParent.noticeBar.showNote("This document is read only.") + self.theParent.updateEditTitle() self.hLight.spellCheck = spTemp qApp.restoreOverrideCursor() diff --git a/nw/gui/elements/doctitlebar.py b/nw/gui/elements/doctitlebar.py index 1d018536..30e4a3db 100644 --- a/nw/gui/elements/doctitlebar.py +++ b/nw/gui/elements/doctitlebar.py @@ -63,6 +63,10 @@ class GuiDocTitleBar(QLabel): return + ## + # Setters + ## + def setTitleFromHandle(self, tHandle): """Sets the document title from the handle, or alternatively, set the whole document path. @@ -91,4 +95,15 @@ class GuiDocTitleBar(QLabel): return True + ## + # Events + ## + + def mousePressEvent(self, theEvent): + """Capture a click on the title and ensure that the item is + selected in the project tree. + """ + self.theParent.treeView.setSelectedHandle(self.theHandle) + return + # END Class GuiDocTitleBar diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index 02ad9155..47c5c276 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -457,6 +457,9 @@ class GuiDocTree(QTreeWidget): return True def getSelectedHandle(self): + """Get the currently selected handle. If multiple items are + selected, return the first. + """ selItem = self.selectedItems() if len(selItem) == 0: return None @@ -465,6 +468,8 @@ class GuiDocTree(QTreeWidget): return None def getSelectedHandles(self): + """Return a list of all currently selected item handles. + """ selItems = self.selectedItems() selHandles = [] for n in range(len(selItems)): @@ -472,6 +477,15 @@ class GuiDocTree(QTreeWidget): selHandles.append(selItems[n].text(self.C_HANDLE)) return selHandles + def setSelectedHandle(self, tHandle): + """Set a specific handle as the selected item. + """ + if tHandle in self.theMap: + self.clearSelection() + self.theMap[tHandle].setSelected(True) + return True + return False + ## # Internal Functions ## diff --git a/nw/gui/elements/docviewer.py b/nw/gui/elements/docviewer.py index 178a54cf..60f60f03 100644 --- a/nw/gui/elements/docviewer.py +++ b/nw/gui/elements/docviewer.py @@ -69,8 +69,12 @@ class GuiDocViewer(QTextBrowser): return def clearViewer(self): + """Clear the content of the document and reset key variables. + """ self.clear() self.setSearchPaths([""]) + self.theHandle = None + self.theParent.updateViewTitle() return True def initViewer(self): @@ -108,6 +112,8 @@ class GuiDocViewer(QTextBrowser): return True def loadText(self, tHandle): + """Load text into the viewer from an item handle. + """ tItem = self.theProject.getItem(tHandle) if tItem is None: @@ -132,7 +138,9 @@ class GuiDocViewer(QTextBrowser): self.theHandle = tHandle self.theProject.setLastViewed(tHandle) + # Make sure the main GUI knows we changed the content self.theParent.viewMeta.refreshReferences(tHandle) + self.theParent.updateViewTitle() return True diff --git a/nw/guimain.py b/nw/guimain.py index c9558787..b2fc4314 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -65,6 +65,11 @@ class GuiMain(QMainWindow): self.hasProject = False self.isZenMode = False + # Init early to avoid circular dependencies + self.docEditor = None + self.docViewer = None + + # Some runtime info useful for debugging logger.info("OS: %s" % self.mainConf.osType) logger.info("Kernel: %s" % self.mainConf.kernelVer) logger.info("Host: %s" % self.mainConf.hostName) @@ -78,13 +83,19 @@ class GuiMain(QMainWindow): self.mainConf.verPyString, self.mainConf.verPyHexVal) ) + # Prepare main window self.resize(*self.mainConf.winGeometry) self._setWindowTitle() self.setWindowIcon(QIcon(self.mainConf.appIcon)) + # Build the GUI + ################ + # Main GUI Elements self.statusBar = GuiMainStatus(self) self.noticeBar = GuiNoticeBar(self) + self.viewTitle = GuiDocTitleBar(self, self.theProject) + self.editTitle = GuiDocTitleBar(self, self.theProject) self.docEditor = GuiDocEditor(self, self.theProject) self.docViewer = GuiDocViewer(self, self.theProject) self.viewMeta = GuiDocViewDetails(self, self.theProject) @@ -93,8 +104,6 @@ class GuiMain(QMainWindow): self.treeView = GuiDocTree(self, self.theProject) self.projView = GuiProjectOutline(self, self.theProject) self.mainMenu = GuiMainMenu(self, self.theProject) - self.viewTitle = GuiDocTitleBar(self, self.theProject) - self.editTitle = GuiDocTitleBar(self, self.theProject) # Minor Gui Elements self.statusIcons = [] @@ -168,7 +177,7 @@ class GuiMain(QMainWindow): self.viewPane.setVisible(False) self.searchBar.setVisible(False) - # Build The Tree View + # Build the Tree View self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) self.rebuildTree() @@ -178,6 +187,9 @@ class GuiMain(QMainWindow): self.setStatusBar(self.statusBar) self.statusBar.setStatus("Ready") + # Finalise Initialisation + ########################## + # Set Up Autosaving Project Timer self.asProjTimer = QTimer() self.asProjTimer.timeout.connect(self._autoSaveProject) @@ -216,6 +228,8 @@ class GuiMain(QMainWindow): logger.debug("GUI initialisation complete") + # Check if a project path was provided at command line, and if + # not, open the project manager instead. if self.mainConf.cmdOpen is not None: logger.debug("Opening project from additional command line option") self.openProject(self.mainConf.cmdOpen) @@ -234,6 +248,8 @@ class GuiMain(QMainWindow): return True def initMain(self): + """Initialise elements that depend on user settings. + """ self.asProjTimer.setInterval(int(self.mainConf.autoSaveProj*1000)) self.asDocTimer.setInterval(int(self.mainConf.autoSaveDoc*1000)) return True @@ -443,6 +459,8 @@ class GuiMain(QMainWindow): return True def backupProject(self): + """Trigger the project backup process. + """ theBackup = NWBackup(self, self.theProject) theBackup.zipIt() return True @@ -458,7 +476,6 @@ class GuiMain(QMainWindow): if self.docEditor.docChanged: self.saveDocument() self.docEditor.clearEditor() - self.editTitle.setTitleFromHandle(None) return True def openDocument(self, tHandle, tLine=None): @@ -470,7 +487,7 @@ class GuiMain(QMainWindow): if self.docEditor.loadText(tHandle, tLine): self.docEditor.setFocus() self.theProject.setLastEdited(tHandle) - self.editTitle.setTitleFromHandle(tHandle) + self.treeView.setSelectedHandle(tHandle) else: return False return True @@ -500,7 +517,6 @@ class GuiMain(QMainWindow): self.tabWidget.setCurrentWidget(self.splitView) if self.docViewer.loadText(tHandle) and not self.viewPane.isVisible(): - self.viewTitle.setTitleFromHandle(tHandle) bPos = self.splitMain.sizes() self.viewPane.setVisible(True) vPos = [0,0] @@ -511,6 +527,9 @@ class GuiMain(QMainWindow): return True def importDocument(self): + """Import the text contained in an out-of-project text file, and + insert the text into the currently open document. + """ lastPath = self.mainConf.lastPath @@ -595,6 +614,24 @@ class GuiMain(QMainWindow): logger.debug("Document action requested, but no document has focus") return True + def updateEditTitle(self): + """Ensure the editor title is up to date with the editor text. + This should only be called by loadText and clearEditor in the + editor class. + """ + if self.docEditor is not None: + self.editTitle.setTitleFromHandle(self.docEditor.theHandle) + return + + def updateViewTitle(self): + """Ensure the viewer title is up to date with the viewer text. + This should only be called by loadText and clearViewer in the + viewer class. + """ + if self.docViewer is not None: + self.viewTitle.setTitleFromHandle(self.docViewer.theHandle) + return + ## # Tree Item Actions ## From bf3ecfbef40419f593000845743002189093e5dc Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 1 May 2020 20:51:46 +0200 Subject: [PATCH 6/7] Moved the document title element into the document instead of main GUI --- nw/gui/elements/doceditor.py | 18 +++++++++++++++--- nw/gui/elements/doctitlebar.py | 22 +++++++++++++++------- nw/gui/elements/docviewer.py | 29 +++++++++++++++++++++++++++-- nw/gui/theme.py | 4 ++++ nw/guimain.py | 30 ++---------------------------- 5 files changed, 63 insertions(+), 40 deletions(-) diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index 5888426c..f09a6083 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -32,7 +32,7 @@ from time import time from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import ( - qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox + qApp, QTextEdit, QAction, QMenu, QShortcut, QMessageBox, QLabel ) from PyQt5.QtGui import ( QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette, @@ -41,6 +41,7 @@ from PyQt5.QtGui import ( from nw.project import NWDoc from nw.gui.tools import GuiDocHighlighter, WordCounter +from nw.gui.elements.doctitlebar import GuiDocTitleBar from nw.tools import NWSpellSimple from nw.constants import nwUnicode, nwDocAction @@ -82,6 +83,10 @@ class GuiDocEditor(QTextEdit): self.qDocument.setDocumentMargin(self.mainConf.textMargin) self.qDocument.contentsChange.connect(self._docChange) + # Document Title + self.docTitle = GuiDocTitleBar(self, self.theProject) + self.docTitle.setGeometry(0,0,self.docTitle.width(),self.docTitle.height()) + # Syntax self.hLight = GuiDocHighlighter(self.qDocument, self.theParent) @@ -150,7 +155,7 @@ class GuiDocEditor(QTextEdit): self.setDocumentChanged(False) self.theParent.noticeBar.hideNote() - self.theParent.updateEditTitle() + self.docTitle.setTitleFromHandle(self.theHandle) return True @@ -265,7 +270,7 @@ class GuiDocEditor(QTextEdit): else: self.theParent.noticeBar.showNote("This document is read only.") - self.theParent.updateEditTitle() + self.docTitle.setTitleFromHandle(self.theHandle) self.hLight.spellCheck = spTemp qApp.restoreOverrideCursor() @@ -321,9 +326,16 @@ class GuiDocEditor(QTextEdit): else: tM = self.mainConf.textMargin + tB = self.lineWidth() + tW = self.width() - 2*tB + tH = self.docTitle.height() + self.docTitle.setGeometry(tB, tB, tW, tH) + docFormat = self.qDocument.rootFrame().frameFormat() docFormat.setLeftMargin(tM) docFormat.setRightMargin(tM) + if docFormat.topMargin() < tH: + docFormat.setTopMargin(tH + 2) # Updating root frame triggers a QTextDocument->contentsChange # signal, which we do not want as it re-runs the syntax diff --git a/nw/gui/elements/doctitlebar.py b/nw/gui/elements/doctitlebar.py index 30e4a3db..6b612910 100644 --- a/nw/gui/elements/doctitlebar.py +++ b/nw/gui/elements/doctitlebar.py @@ -30,7 +30,7 @@ import nw from PyQt5.QtCore import Qt from PyQt5.QtGui import QPalette, QColor -from PyQt5.QtWidgets import QLabel +from PyQt5.QtWidgets import QLabel, QFrame, QStyle from nw.constants import nwUnicode @@ -50,14 +50,22 @@ class GuiDocTitleBar(QLabel): self.theHandle = None self.setText("") - self.setContentsMargins(8,2,8,2) + self.setIndent(0) + self.setMargin(0) + self.setContentsMargins(0,0,0,0) self.setAutoFillBackground(True) self.setAlignment(Qt.AlignCenter) self.setWordWrap(True) - docPalette = self.palette() - docPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) - docPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) - self.setPalette(docPalette) + self.setFrameShape(QFrame.NoFrame) + self.setLineWidth(0) + lblPalette = self.palette() + lblPalette.setColor(QPalette.Window, QColor(*self.theTheme.colBack)) + lblPalette.setColor(QPalette.Text, QColor(*self.theTheme.colText)) + self.setPalette(lblPalette) + + lblFont = self.font() + lblFont.setPointSizeF(0.9*self.theTheme.defFontSize) + self.setFont(lblFont) logger.debug("DocTitleBar initialisation complete") @@ -103,7 +111,7 @@ class GuiDocTitleBar(QLabel): """Capture a click on the title and ensure that the item is selected in the project tree. """ - self.theParent.treeView.setSelectedHandle(self.theHandle) + self.theParent.theParent.treeView.setSelectedHandle(self.theHandle) return # END Class GuiDocTitleBar diff --git a/nw/gui/elements/docviewer.py b/nw/gui/elements/docviewer.py index 60f60f03..82434adc 100644 --- a/nw/gui/elements/docviewer.py +++ b/nw/gui/elements/docviewer.py @@ -34,6 +34,7 @@ from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor, QTextCursor from nw.convert import ToHtml from nw.constants import nwAlert, nwItemType, nwDocAction +from nw.gui.elements.doctitlebar import GuiDocTitleBar logger = logging.getLogger(__name__) @@ -56,6 +57,10 @@ class GuiDocViewer(QTextBrowser): self.setOpenExternalLinks(False) self.initViewer() + # Document Title + self.docTitle = GuiDocTitleBar(self, self.theProject) + self.docTitle.setGeometry(0,0,self.docTitle.width(),self.docTitle.height()) + theOpt = QTextOption() if self.mainConf.doJustify: theOpt.setAlignment(Qt.AlignJustify) @@ -74,7 +79,7 @@ class GuiDocViewer(QTextBrowser): self.clear() self.setSearchPaths([""]) self.theHandle = None - self.theParent.updateViewTitle() + self.docTitle.setTitleFromHandle(self.theHandle) return True def initViewer(self): @@ -137,10 +142,10 @@ class GuiDocViewer(QTextBrowser): self.verticalScrollBar().setValue(sPos) self.theHandle = tHandle self.theProject.setLastViewed(tHandle) + self.docTitle.setTitleFromHandle(self.theHandle) # Make sure the main GUI knows we changed the content self.theParent.viewMeta.refreshReferences(tHandle) - self.theParent.updateViewTitle() return True @@ -184,6 +189,26 @@ class GuiDocViewer(QTextBrowser): return False return True + ## + # Events + ## + + def resizeEvent(self, theEvent): + """Make sure the document title is the same width as the window. + """ + QTextBrowser.resizeEvent(self, theEvent) + + tB = self.lineWidth() + tW = self.width() - 2*tB + tH = self.docTitle.height() + self.docTitle.setGeometry(tB, tB, tW, tH) + + docFormat = self.qDocument.rootFrame().frameFormat() + if docFormat.topMargin() < tH: + docFormat.setTopMargin(tH + 2) + + return + ## # Internal Functions ## diff --git a/nw/gui/theme.py b/nw/gui/theme.py index 95c318d8..00463aac 100644 --- a/nw/gui/theme.py +++ b/nw/gui/theme.py @@ -110,6 +110,10 @@ class GuiTheme: self.getPixmap = self.theIcons.getPixmap self.loadDecoration = self.theIcons.loadDecoration + # Extract Other Info + self.defFont = qApp.font() + self.defFontSize = self.defFont.pointSizeF() + return ## diff --git a/nw/guimain.py b/nw/guimain.py index b2fc4314..a533526f 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -43,7 +43,7 @@ from nw.gui import ( GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor, GuiExport, GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails, GuiConfigEditor, GuiProjectEditor, GuiItemEditor, GuiProjectOutline, - GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad, GuiDocTitleBar + GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad ) from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup from nw.tools import countWords @@ -65,10 +65,6 @@ class GuiMain(QMainWindow): self.hasProject = False self.isZenMode = False - # Init early to avoid circular dependencies - self.docEditor = None - self.docViewer = None - # Some runtime info useful for debugging logger.info("OS: %s" % self.mainConf.osType) logger.info("Kernel: %s" % self.mainConf.kernelVer) @@ -94,8 +90,6 @@ class GuiMain(QMainWindow): # Main GUI Elements self.statusBar = GuiMainStatus(self) self.noticeBar = GuiNoticeBar(self) - self.viewTitle = GuiDocTitleBar(self, self.theProject) - self.editTitle = GuiDocTitleBar(self, self.theProject) self.docEditor = GuiDocEditor(self, self.theProject) self.docViewer = GuiDocViewer(self, self.theProject) self.viewMeta = GuiDocViewDetails(self, self.theProject) @@ -123,7 +117,6 @@ class GuiMain(QMainWindow): self.docEdit.setSpacing(0) self.docEdit.addWidget(self.searchBar) self.docEdit.addWidget(self.noticeBar) - self.docEdit.addWidget(self.editTitle) self.docEdit.addWidget(self.docEditor) self.editPane.setLayout(self.docEdit) @@ -131,10 +124,9 @@ class GuiMain(QMainWindow): self.docView = QVBoxLayout() self.docView.setContentsMargins(0,0,0,0) self.docView.setSpacing(0) - self.docView.addWidget(self.viewTitle) self.docView.addWidget(self.docViewer) self.docView.addWidget(self.viewMeta) - self.docView.setStretch(1, 1) + self.docView.setStretch(0, 1) self.viewPane.setLayout(self.docView) self.splitView = QSplitter(Qt.Horizontal) @@ -614,24 +606,6 @@ class GuiMain(QMainWindow): logger.debug("Document action requested, but no document has focus") return True - def updateEditTitle(self): - """Ensure the editor title is up to date with the editor text. - This should only be called by loadText and clearEditor in the - editor class. - """ - if self.docEditor is not None: - self.editTitle.setTitleFromHandle(self.docEditor.theHandle) - return - - def updateViewTitle(self): - """Ensure the viewer title is up to date with the viewer text. - This should only be called by loadText and clearViewer in the - viewer class. - """ - if self.docViewer is not None: - self.viewTitle.setTitleFromHandle(self.docViewer.theHandle) - return - ## # Tree Item Actions ## From b44669a3b5a9c143f3006fed5a271d6e58243086 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 1 May 2020 21:30:18 +0200 Subject: [PATCH 7/7] Some minor tweaks, and added option for showing full path --- nw/config.py | 5 +++++ nw/gui/elements/doceditor.py | 3 +++ nw/gui/elements/doctitlebar.py | 4 ++-- nw/gui/elements/docviewer.py | 3 +++ nw/guimain.py | 6 +++--- tests/reference/novelwriter.conf | 3 ++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/nw/config.py b/nw/config.py index ada60d85..5c8388b4 100644 --- a/nw/config.py +++ b/nw/config.py @@ -116,6 +116,7 @@ class Config: self.showTabsNSpaces = False self.showLineEndings = False self.bigDocLimit = 800 + self.showFullPath = True self.fmtApostrophe = nwUnicode.U_RSQUO self.fmtSingleQuotes = [nwUnicode.U_LSQUO,nwUnicode.U_RSQUO] @@ -398,6 +399,9 @@ class Config: self.bigDocLimit = self._parseLine( cnfParse, cnfSec, "bigdoclimit", self.CNF_INT, self.bigDocLimit ) + self.showFullPath = self._parseLine( + cnfParse, cnfSec, "showfullpath", self.CNF_BOOL, self.showFullPath + ) ## Backup cnfSec = "Backup" @@ -486,6 +490,7 @@ class Config: cnfParse.set(cnfSec,"showtabsnspaces", str(self.showTabsNSpaces)) cnfParse.set(cnfSec,"showlineendings", str(self.showLineEndings)) cnfParse.set(cnfSec,"bigdoclimit", str(self.bigDocLimit)) + cnfParse.set(cnfSec,"showfullpath", str(self.showFullPath)) ## Backup cnfSec = "Backup" diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index f09a6083..fb76ff67 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -132,6 +132,9 @@ class GuiDocEditor(QTextEdit): logger.debug("DocEditor initialisation complete") + # Connect Functions + self.setSelectedHandle = self.theParent.treeView.setSelectedHandle + return def clearEditor(self): diff --git a/nw/gui/elements/doctitlebar.py b/nw/gui/elements/doctitlebar.py index 6b612910..b8d5d739 100644 --- a/nw/gui/elements/doctitlebar.py +++ b/nw/gui/elements/doctitlebar.py @@ -85,7 +85,7 @@ class GuiDocTitleBar(QLabel): if tHandle is None: return False - if True: + if self.mainConf.showFullPath: tTitle = [] tTree = self.theProject.getItemPath(tHandle) for aHandle in reversed(tTree): @@ -111,7 +111,7 @@ class GuiDocTitleBar(QLabel): """Capture a click on the title and ensure that the item is selected in the project tree. """ - self.theParent.theParent.treeView.setSelectedHandle(self.theHandle) + self.theParent.setSelectedHandle(self.theHandle) return # END Class GuiDocTitleBar diff --git a/nw/gui/elements/docviewer.py b/nw/gui/elements/docviewer.py index 82434adc..84f4a8fd 100644 --- a/nw/gui/elements/docviewer.py +++ b/nw/gui/elements/docviewer.py @@ -71,6 +71,9 @@ class GuiDocViewer(QTextBrowser): logger.debug("DocViewer initialisation complete") + # Connect Functions + self.setSelectedHandle = self.theParent.treeView.setSelectedHandle + return def clearViewer(self): diff --git a/nw/guimain.py b/nw/guimain.py index a533526f..0b6ac0a5 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -90,12 +90,12 @@ class GuiMain(QMainWindow): # Main GUI Elements self.statusBar = GuiMainStatus(self) self.noticeBar = GuiNoticeBar(self) + self.treeView = GuiDocTree(self, self.theProject) self.docEditor = GuiDocEditor(self, self.theProject) self.docViewer = GuiDocViewer(self, self.theProject) self.viewMeta = GuiDocViewDetails(self, self.theProject) self.searchBar = GuiSearchBar(self) self.treeMeta = GuiDocDetails(self, self.theProject) - self.treeView = GuiDocTree(self, self.theProject) self.projView = GuiProjectOutline(self, self.theProject) self.mainMenu = GuiMainMenu(self, self.theProject) @@ -114,7 +114,7 @@ class GuiMain(QMainWindow): self.editPane = QFrame() self.docEdit = QVBoxLayout() self.docEdit.setContentsMargins(0,0,0,0) - self.docEdit.setSpacing(0) + self.docEdit.setSpacing(2) self.docEdit.addWidget(self.searchBar) self.docEdit.addWidget(self.noticeBar) self.docEdit.addWidget(self.docEditor) @@ -123,7 +123,7 @@ class GuiMain(QMainWindow): self.viewPane = QFrame() self.docView = QVBoxLayout() self.docView.setContentsMargins(0,0,0,0) - self.docView.setSpacing(0) + self.docView.setSpacing(2) self.docView.addWidget(self.docViewer) self.docView.addWidget(self.viewMeta) self.docView.setStretch(0, 1) diff --git a/tests/reference/novelwriter.conf b/tests/reference/novelwriter.conf index ad20b354..7102783d 100644 --- a/tests/reference/novelwriter.conf +++ b/tests/reference/novelwriter.conf @@ -1,5 +1,5 @@ [Main] -timestamp = 2020-04-14 22:47:16 +timestamp = 2020-05-01 21:28:25 theme = default syntax = default_light guidark = False @@ -38,6 +38,7 @@ spellcheck = en showtabsnspaces = False showlineendings = False bigdoclimit = 800 +showfullpath = True [Backup] backuppath =