diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 283b31dd..eae88de0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,7 +70,7 @@ The ignored errors are all `pycodestyle` errors, and they are documented [here](https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes). **E203:** whitespace before ‘:’ -**Reason:** Column alignment. It is natural to align dictionary columns along the `:` character, +**Reason:** Column alignment. It is natural to align dictionary columns along the `:` character. **E221:** multiple spaces before operator **Reason:** Column alignment. @@ -88,15 +88,15 @@ dropped. Don't use the `+` operator for appending multiple strings. Use formatti **Reason:** Column alignment. **E261:** at least two spaces before inline comment -**Reason:** With syntax highlighting, this one is meaningless and wastes an extra space. +**Reason:** With syntax highlighting, this one doesn't make much sense. **E266:** too many leading ‘#’ for block comment -**Reason:** In the source multiple `#`s to indicate importance levels is sometimes used, like -markdown headers. +**Reason:** In the source multiple `#`s is sometimes used to indicate importance or heading level, +like markdown headers. **E302:** expected 2 blank lines, found 0 **Reason:** Applies to classes. Instead, end classes with a comment like `# END Class ClassName` to -make it easier to se which class just ended. The double line break is then redundant. +make it easier to see which class just ended. The double line break is then redundant. **E305:** expected 2 blank lines after end of function or class **Reason:** Instead, _always_ end a function with a `return`, preferrably indented at function diff --git a/README.md b/README.md index 78849745..f80342b4 100644 --- a/README.md +++ b/README.md @@ -240,13 +240,6 @@ clickable in the document view pane, and control-clickable in the editor. They m quickly navigate between the documents while editing. -## Contributing - -If you want to contribute to novelWriter, please follow the coding convention laid out in the -[Style Guide](markdown/style.md). They broadly follow Python PEP8, but there are a few -modifications. - - ## Screenshot **novelWriter with default system theme:** diff --git a/nw/assets/icons/fallback/bullet-off-dark.svg b/nw/assets/icons/fallback/bullet-off-dark.svg new file mode 100644 index 00000000..46e56070 --- /dev/null +++ b/nw/assets/icons/fallback/bullet-off-dark.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/fallback/bullet-off.svg b/nw/assets/icons/fallback/bullet-off.svg new file mode 100644 index 00000000..2e2712be --- /dev/null +++ b/nw/assets/icons/fallback/bullet-off.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/fallback/bullet-on-dark.svg b/nw/assets/icons/fallback/bullet-on-dark.svg new file mode 100644 index 00000000..ba3d9739 --- /dev/null +++ b/nw/assets/icons/fallback/bullet-on-dark.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/fallback/bullet-on.svg b/nw/assets/icons/fallback/bullet-on.svg new file mode 100644 index 00000000..7a077ddd --- /dev/null +++ b/nw/assets/icons/fallback/bullet-on.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 a5930cfc..927da636 100644 --- a/nw/assets/icons/typicons_colour_dark/icons.conf +++ b/nw/assets/icons/typicons_colour_dark/icons.conf @@ -55,3 +55,5 @@ backward = chevron-left.svg forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg +bullet-on = media-record.svg +bullet-off = media-record-outline.svg diff --git a/nw/assets/icons/typicons_colour_dark/media-record-outline.svg b/nw/assets/icons/typicons_colour_dark/media-record-outline.svg new file mode 100644 index 00000000..237dac60 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/media-record-outline.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/media-record.svg b/nw/assets/icons/typicons_colour_dark/media-record.svg new file mode 100644 index 00000000..8f7789b1 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/media-record.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 934ad1d6..1a97029f 100644 --- a/nw/assets/icons/typicons_colour_light/icons.conf +++ b/nw/assets/icons/typicons_colour_light/icons.conf @@ -55,3 +55,5 @@ backward = chevron-left.svg forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg +bullet-on = media-record.svg +bullet-off = media-record-outline.svg diff --git a/nw/assets/icons/typicons_colour_light/media-record-outline.svg b/nw/assets/icons/typicons_colour_light/media-record-outline.svg new file mode 100644 index 00000000..29041550 --- /dev/null +++ b/nw/assets/icons/typicons_colour_light/media-record-outline.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_light/media-record.svg b/nw/assets/icons/typicons_colour_light/media-record.svg new file mode 100644 index 00000000..c51c3bef --- /dev/null +++ b/nw/assets/icons/typicons_colour_light/media-record.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 81829f57..f9c8c49d 100644 --- a/nw/assets/icons/typicons_grey_dark/icons.conf +++ b/nw/assets/icons/typicons_grey_dark/icons.conf @@ -55,3 +55,5 @@ backward = chevron-left.svg forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg +bullet-on = media-record.svg +bullet-off = media-record-outline.svg diff --git a/nw/assets/icons/typicons_grey_dark/media-record-outline.svg b/nw/assets/icons/typicons_grey_dark/media-record-outline.svg new file mode 100644 index 00000000..46e56070 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/media-record-outline.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/media-record.svg b/nw/assets/icons/typicons_grey_dark/media-record.svg new file mode 100644 index 00000000..ba3d9739 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/media-record.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 145dc153..732486a5 100644 --- a/nw/assets/icons/typicons_grey_light/icons.conf +++ b/nw/assets/icons/typicons_grey_light/icons.conf @@ -55,3 +55,5 @@ backward = chevron-left.svg forward = chevron-right.svg sticky-on = pin.svg sticky-off = pin-outline.svg +bullet-on = media-record.svg +bullet-off = media-record-outline.svg diff --git a/nw/assets/icons/typicons_grey_light/media-record-outline.svg b/nw/assets/icons/typicons_grey_light/media-record-outline.svg new file mode 100644 index 00000000..2e2712be --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/media-record-outline.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/media-record.svg b/nw/assets/icons/typicons_grey_light/media-record.svg new file mode 100644 index 00000000..7a077ddd --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/media-record.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/config.py b/nw/config.py index 3c3915b7..6e0a34b3 100644 --- a/nw/config.py +++ b/nw/config.py @@ -812,6 +812,16 @@ class Config: self.errData = [] return errMessage + def setViewComments(self, viewState): + self.viewComments = viewState + self.confChanged = True + return self.viewComments + + def setViewSynopsis(self, viewState): + self.viewSynopsis = viewState + self.confChanged = True + return self.viewSynopsis + ## # Getters ## diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 5ebdf19e..0da72625 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -652,6 +652,15 @@ class GuiDocEditor(QTextEdit): self.docSearch.closeSearch() return self.docSearch.isVisible() + def toggleSearch(self): + """Toggle the visibility of the search box. + """ + if self.docSearch.isVisible(): + self.docSearch.closeSearch() + else: + self._beginSearch() + return + ## # Document Events and Maintenance ## @@ -1853,7 +1862,6 @@ class GuiDocEditHeader(QWidget): self.buttonSize = fPx + hSp # Main Widget Settings - self.setContentsMargins(2*self.buttonSize, 0, 0, 0) self.setAutoFillBackground(True) self.setPalette(self.thePalette) @@ -1878,6 +1886,28 @@ class GuiDocEditHeader(QWidget): ).format(*self.theTheme.colText) # Buttons + self.editButton = QToolButton(self) + self.editButton.setIcon(self.theTheme.getIcon("edit")) + self.editButton.setContentsMargins(0, 0, 0, 0) + self.editButton.setIconSize(QSize(fPx, fPx)) + self.editButton.setFixedSize(fPx, fPx) + self.editButton.setStyleSheet(buttonStyle) + self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.editButton.setVisible(False) + self.editButton.setToolTip("Edit document meta") + self.editButton.clicked.connect(self._editDocument) + + self.searchButton = QToolButton(self) + self.searchButton.setIcon(self.theTheme.getIcon("search")) + self.searchButton.setContentsMargins(0, 0, 0, 0) + self.searchButton.setIconSize(QSize(fPx, fPx)) + self.searchButton.setFixedSize(fPx, fPx) + self.searchButton.setStyleSheet(buttonStyle) + self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.searchButton.setVisible(False) + self.searchButton.setToolTip("Search document") + self.searchButton.clicked.connect(self._searchDocument) + self.minmaxButton = QToolButton(self) self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) self.minmaxButton.setContentsMargins(0, 0, 0, 0) @@ -1903,6 +1933,8 @@ class GuiDocEditHeader(QWidget): # Assemble Layout self.outerBox = QHBoxLayout() self.outerBox.setSpacing(hSp) + self.outerBox.addWidget(self.editButton, 0) + self.outerBox.addWidget(self.searchButton, 0) self.outerBox.addWidget(self.theTitle, 1) self.outerBox.addWidget(self.minmaxButton, 0) self.outerBox.addWidget(self.closeButton, 0) @@ -1923,6 +1955,8 @@ class GuiDocEditHeader(QWidget): self.theHandle = tHandle if tHandle is None: self.theTitle.setText("") + self.editButton.setVisible(False) + self.searchButton.setVisible(False) self.closeButton.setVisible(False) self.minmaxButton.setVisible(False) return True @@ -1942,6 +1976,8 @@ class GuiDocEditHeader(QWidget): return False self.theTitle.setText(nwItem.itemName) + self.editButton.setVisible(True) + self.searchButton.setVisible(True) self.closeButton.setVisible(True) self.minmaxButton.setVisible(True) @@ -1951,10 +1987,24 @@ class GuiDocEditHeader(QWidget): # Slots ## + def _editDocument(self): + """Open the edit item dialog from the main GUI. + """ + self.theParent.editItem(self.theHandle) + return + + def _searchDocument(self): + """Toggle the visibility of the search box. + """ + self.docEditor.toggleSearch() + return + def _closeDocument(self): - """Trigger the close editor/viewer on the main window. + """Trigger the close editor on the main window. """ self.theParent.closeDocEditor() + self.editButton.setVisible(False) + self.searchButton.setVisible(False) self.closeButton.setVisible(False) self.minmaxButton.setVisible(False) return @@ -1966,10 +2016,14 @@ class GuiDocEditHeader(QWidget): if self.theParent.isFocusMode: self.minmaxButton.setIcon(self.theTheme.getIcon("minimise")) self.setContentsMargins(self.buttonSize, 0, 0, 0) + self.editButton.setVisible(False) + self.searchButton.setVisible(False) self.closeButton.setVisible(False) else: self.minmaxButton.setIcon(self.theTheme.getIcon("maximise")) - self.setContentsMargins(2*self.buttonSize, 0, 0, 0) + self.setContentsMargins(0, 0, 0, 0) + self.editButton.setVisible(True) + self.searchButton.setVisible(True) self.closeButton.setVisible(True) return diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index e075f80b..c54411b4 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -202,7 +202,7 @@ class GuiDocViewer(QTextBrowser): def reloadText(self): """Reload the text in the current document. """ - self.loadText(self.theHandle) + self.loadText(self.theHandle, updateHistory=False) return def loadFromTag(self, theTag): @@ -879,6 +879,19 @@ class GuiDocViewFooter(QWidget): bSp = self.mainConf.pxInt(2) hSp = self.mainConf.pxInt(8) + # Icons + 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) + + bulletOn = self.theTheme.getPixmap("bullet-on", (fPx, fPx)) + bulletOff = self.theTheme.getPixmap("bullet-off", (fPx, fPx)) + bulletIcon = QIcon() + bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On) + bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off) + # Main Widget Settings self.setContentsMargins(0, 0, 0, 0) self.setAutoFillBackground(True) @@ -900,11 +913,6 @@ class GuiDocViewFooter(QWidget): self.showHide.setToolTip("Show/hide the references panel") # 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) @@ -917,6 +925,30 @@ class GuiDocViewFooter(QWidget): "Activate to freeze the content of the references panel when changing document" ) + # Show Comments + self.showComments = QToolButton(self) + self.showComments.setCheckable(True) + self.showComments.setChecked(self.mainConf.viewComments) + self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.showComments.setStyleSheet(buttonStyle) + self.showComments.setIcon(bulletIcon) + self.showComments.setIconSize(QSize(fPx, fPx)) + self.showComments.setFixedSize(QSize(fPx, fPx)) + self.showComments.toggled.connect(self._doToggleComments) + self.showComments.setToolTip("Show comments") + + # Show Synopsis + self.showSynopsis = QToolButton(self) + self.showSynopsis.setCheckable(True) + self.showSynopsis.setChecked(self.mainConf.viewSynopsis) + self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.showSynopsis.setStyleSheet(buttonStyle) + self.showSynopsis.setIcon(bulletIcon) + self.showSynopsis.setIconSize(QSize(fPx, fPx)) + self.showSynopsis.setFixedSize(QSize(fPx, fPx)) + self.showSynopsis.toggled.connect(self._doToggleSynopsis) + self.showSynopsis.setToolTip("Show synopsis comments") + # Labels self.lblRefs = QLabel("References") self.lblRefs.setBuddy(self.showHide) @@ -938,10 +970,32 @@ class GuiDocViewFooter(QWidget): self.lblSticky.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.lblSticky.setPalette(self.thePalette) + self.lblComments = QLabel("Comments") + self.lblComments.setBuddy(self.showComments) + self.lblComments.setIndent(0) + self.lblComments.setMargin(0) + self.lblComments.setContentsMargins(0, 0, 0, 0) + self.lblComments.setAutoFillBackground(True) + self.lblComments.setFixedHeight(fPx) + self.lblComments.setAlignment(Qt.AlignLeft | Qt.AlignTop) + self.lblComments.setPalette(self.thePalette) + + self.lblSynopsis = QLabel("Synopsis") + self.lblSynopsis.setBuddy(self.showSynopsis) + self.lblSynopsis.setIndent(0) + self.lblSynopsis.setMargin(0) + self.lblSynopsis.setContentsMargins(0, 0, 0, 0) + self.lblSynopsis.setAutoFillBackground(True) + self.lblSynopsis.setFixedHeight(fPx) + self.lblSynopsis.setAlignment(Qt.AlignLeft | Qt.AlignTop) + self.lblSynopsis.setPalette(self.thePalette) + lblFont = self.font() lblFont.setPointSizeF(0.9*self.theTheme.fontPointSize) self.lblRefs.setFont(lblFont) self.lblSticky.setFont(lblFont) + self.lblComments.setFont(lblFont) + self.lblSynopsis.setFont(lblFont) # Assemble Layout self.outerBox = QHBoxLayout() @@ -952,6 +1006,11 @@ class GuiDocViewFooter(QWidget): self.outerBox.addWidget(self.stickyRefs, 0) self.outerBox.addWidget(self.lblSticky, 0) self.outerBox.addStretch(1) + self.outerBox.addWidget(self.showComments, 0) + self.outerBox.addWidget(self.lblComments, 0) + self.outerBox.addSpacing(hSp) + self.outerBox.addWidget(self.showSynopsis, 0) + self.outerBox.addWidget(self.lblSynopsis, 0) self.setLayout(self.outerBox) logger.debug("GuiDocViewFooter initialisation complete") @@ -978,6 +1037,20 @@ class GuiDocViewFooter(QWidget): self.viewMeta.refreshReferences(self.docViewer.theHandle) return + def _doToggleComments(self, theState): + """Toggle the view comment button and reload the document. + """ + self.mainConf.setViewComments(theState) + self.docViewer.reloadText() + return + + def _doToggleSynopsis(self, theState): + """Toggle the view synopsis button and reload the document. + """ + self.mainConf.setViewSynopsis(theState) + self.docViewer.reloadText() + return + # END Class GuiDocViewFooter # =============================================================================================== # diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index e7052671..d1e21c20 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -490,32 +490,11 @@ class GuiConfigEditLayoutTab(QWidget): self.tabWidth.setSingleStep(1) self.tabWidth.setValue(self.mainConf.tabWidth) self.mainForm.addRow( - "Editor tab width", + "Document tab width", self.tabWidth, - "This feature requires Qt 5.9 or later.", theUnit="px" ) - # Render Options - # ============== - self.mainForm.addGroupLabel("Render Text") - - ## Render Comments - self.viewComments = QSwitch() - self.viewComments.setChecked(self.mainConf.viewComments) - self.mainForm.addRow( - "Render comments in document view panel", - self.viewComments - ) - - ## Render Synopsis - self.viewSynopsis = QSwitch() - self.viewSynopsis.setChecked(self.mainConf.viewSynopsis) - self.mainForm.addRow( - "Render synopsis in document view panel", - self.viewSynopsis - ) - return def saveValues(self): @@ -533,8 +512,6 @@ class GuiConfigEditLayoutTab(QWidget): doJustify = self.textJustify.isChecked() textMargin = self.textMargin.value() tabWidth = self.tabWidth.value() - viewComments = self.viewComments.isChecked() - viewSynopsis = self.viewSynopsis.isChecked() self.mainConf.textFont = textFont self.mainConf.textSize = textSize @@ -545,8 +522,6 @@ class GuiConfigEditLayoutTab(QWidget): self.mainConf.doJustify = doJustify self.mainConf.textMargin = textMargin self.mainConf.tabWidth = tabWidth - self.mainConf.viewComments = viewComments - self.mainConf.viewSynopsis = viewSynopsis self.mainConf.confChanged = True diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 36130b1f..28d23102 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -237,7 +237,7 @@ class GuiProjectTree(QTreeWidget): # Add the new item to the tree self.revealTreeItem(tHandle, nHandle) - self.theParent.editItem() + self.theParent.editItem(tHandle) return True diff --git a/nw/gui/theme.py b/nw/gui/theme.py index 9abfb523..123063b0 100644 --- a/nw/gui/theme.py +++ b/nw/gui/theme.py @@ -560,6 +560,8 @@ class GuiIcons: ## Switches "sticky-on" : (None, None), "sticky-off" : (None, None), + "bullet-on" : (None, None), + "bullet-off" : (None, None), } DECO_MAP = { diff --git a/nw/guimain.py b/nw/guimain.py index 73209394..de340304 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -674,10 +674,11 @@ class GuiMain(QMainWindow): return True - def editItem(self): + def editItem(self, tHandle=None): """Open the edit item dialog. """ - tHandle = self.treeView.getSelectedHandle() + if tHandle is None: + tHandle = self.treeView.getSelectedHandle() if tHandle is None: logger.warning("No item selected") return