Merge pull request #419 from vkbo/viewer_tabs

Tab Stop in Document Viewer and Build
This commit is contained in:
Veronica K. Berglyd Olsen
2020-08-18 20:18:00 +02:00
committed by GitHub
5 changed files with 68 additions and 28 deletions
+18 -15
View File
@@ -9,10 +9,10 @@
<img align="left" style="margin: 0 16px 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/assets/icons/96x96/novelwriter.png"> <img align="left" style="margin: 0 16px 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/assets/icons/96x96/novelwriter.png">
novelWriter is a markdown-like text editor designed for writing novels and larger projects of many novelWriter is a Markdown-like text editor designed for writing novels and larger projects of many
smaller plain text documents. It uses its own flavour of markdown that supports a meta data syntax smaller plain text documents. It uses its own flavour of Markdown that supports a meta data syntax
for comments, synopsis and cross-referencing between files. It's designed to be a simple text editor for comments, synopsis and cross-referencing between files. It's designed to be a simple text editor
which allows for easy organisation of text files and notes, built on plain text files for that allows for easy organisation of text files and notes, built on plain text files for
robustness. robustness.
The plain text storage is suitable for version control software, and also well suited for file The plain text storage is suitable for version control software, and also well suited for file
@@ -29,7 +29,7 @@ The contributing guide is available in [CONTRIBUTING](CONTRIBUTING.md).
The default branch on this repository switched to `main` on 6. August 2020. If you are running The default branch on this repository switched to `main` on 6. August 2020. If you are running
novelWriter from a git clone, you need to clone the repository again. novelWriter from a git clone, you need to clone the repository again.
Alternatively, you can run the following to get back on the main branch: Alternatively, you can run the following to get back on the new default branch:
```bash ```bash
git remote update git remote update
@@ -48,8 +48,8 @@ novelWriter is in a _beta_ state. Please report any issues you may encounter in
tracker. tracker.
You should be able to use novelWriter for real projects, but as with all software, please make You should be able to use novelWriter for real projects, but as with all software, please make
regular backups. There is a built in backup feature that can pack the entire project into a zip file regular backups of your data. There is a built in backup feature that can pack the entire project
on close. Please check the documentation for further details. into a zip file on close. Please check the documentation for further details.
## License ## License
@@ -80,7 +80,7 @@ novelWriter is _not_ a full-feature Markdown editor. It allows for a minimal set
needed for writing text documents for novels. These are currently limited to: needed for writing text documents for novels. These are currently limited to:
* Headings level 1 to 4 using the `#` syntax only. * Headings level 1 to 4 using the `#` syntax only.
* Emphasised, strong text. These are rendered as italicised and bold. * Emphasised and strong text. These are rendered as italicised and bold.
* Strikethrough text. * Strikethrough text.
* Hard line breaks using two or more spaces at the end of a line. * Hard line breaks using two or more spaces at the end of a line.
@@ -98,21 +98,24 @@ In addition, novelWriter adds the following, which is otherwise not supported by
version, non-breaking spaces are converted to normal spaces when saving the document. This is done version, non-breaking spaces are converted to normal spaces when saving the document. This is done
by the Qt library. by the Qt library.
* Thin spaces are also supported, as well as non-breaking thin spaces. * Thin spaces are also supported, as well as non-breaking thin spaces.
* Tabs may be rendered, depending on export format. With Qt 5.10 or higher, the width of a tab in * Tabs can be used in the text, and should be properly aligned. The width of a tab in pixels can be
pixels can be changed in Preferences. changed in Preferences. Note that tabs are exported as-is, also to HTML format. However, most
browsers will treat a tab as a space, so it may not show up like expected if you view the exported
HTML file.
The core export format of novelWriter is HTML5. You can also export the entire project as a single The core export format of novelWriter is HTML5. You can also export the entire project as a single
novelWriter flavour document. In addition, other exports to Open Document, PDF, and plain text is novelWriter Markdown-flavour document. In addition, other exports to Open Document, PDF, and plain
offered through the Qt library, although with limitations to formatting. text is offered through the Qt library, although with limitations to formatting.
## Implementation ## Implementation
The application is written in Python3 using Qt5 via PyQt5. It is developed on Linux, but it should The application is written in Python3 using Qt5 via PyQt5. It is developed on Linux, but it should
in principle work fine on other operating systems as well as long as dependencies are met. It is in principle work fine on other operating systems as well, as long as dependencies are met. It is
regularly tested on Windows 10. regularly tested on Windows 10.
The application can be started from the source folder with one of the commands: The application can be started from the source folder with one of the commands, depending on your
Python configuration:
``` ```
./novelWriter.py ./novelWriter.py
python novelWriter.py python novelWriter.py
@@ -158,7 +161,7 @@ pip install pyenchant
``` ```
PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work. PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work.
Exporting to markdown requires PyQt/Qt 5.14. There are no known minimum for `lxml`, but the code Exporting to Markdown requires PyQt/Qt 5.14. There are no known minimum for `lxml`, but the code
was originally written with 4.2. The optional spell check library must be at least 3.0.0 to work was originally written with 4.2. The optional spell check library must be at least 3.0.0 to work
with Windows 64 bit systems. On Linux, 2.0.0 also works fine. with Windows 64 bit systems. On Linux, 2.0.0 also works fine.
@@ -237,7 +240,7 @@ clickable in the document view pane, and control-clickable in the editor. They m
quickly navigate between the documents while editing. quickly navigate between the documents while editing.
## Contribution ## Contributing
If you want to contribute to novelWriter, please follow the coding convention laid out in the 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 [Style Guide](markdown/style.md). They broadly follow Python PEP8, but there are a few
+3 -5
View File
@@ -45,10 +45,9 @@ class ToHtml(Tokenizer):
self.cssStyles = True self.cssStyles = True
self.repDict = { self.repDict = {
"<" : "&lt;", "<" : "&lt;",
">" : "&gt;", ">" : "&gt;",
"&" : "&amp;", "&" : "&amp;",
"\t" : "&emsp;"*2,
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH, nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH, nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP, nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
@@ -77,7 +76,6 @@ class ToHtml(Tokenizer):
self.doKeywords = True self.doKeywords = True
self.doComments = doComments self.doComments = doComments
self.doSynopsis = doSynopsis self.doSynopsis = doSynopsis
self.repDict["\t"] = "&nbsp;"*8
self._buildRegEx() self._buildRegEx()
return return
+22 -2
View File
@@ -551,7 +551,6 @@ class GuiBuildNovel(QDialog):
isNone |= theItem.itemClass == nwItemClass.NO_CLASS isNone |= theItem.itemClass == nwItemClass.NO_CLASS
isNone |= theItem.itemClass == nwItemClass.TRASH isNone |= theItem.itemClass == nwItemClass.TRASH
isNone |= theItem.parHandle == self.theProject.projTree.trashRoot() isNone |= theItem.parHandle == self.theProject.projTree.trashRoot()
isNone |= theItem.parHandle == self.theProject.projTree.archiveRoot()
isNone |= theItem.parHandle is None isNone |= theItem.parHandle is None
isNote = theItem.itemLayout == nwItemLayout.NOTE isNote = theItem.itemLayout == nwItemLayout.NOTE
isNovel = not isNone and not isNote isNovel = not isNone and not isNote
@@ -563,6 +562,10 @@ class GuiBuildNovel(QDialog):
if isNovel and not novelFiles: if isNovel and not novelFiles:
return False return False
rootItem = self.theProject.projTree.getRootItem(theItem.itemHandle)
if rootItem.itemClass == nwItemClass.ARCHIVE:
return False
return True return True
def _saveDocument(self, theFormat): def _saveDocument(self, theFormat):
@@ -1014,13 +1017,30 @@ class GuiBuildNovelDocView(QTextBrowser):
theText = "".join(theText) theText = "".join(theText)
self.buildTime = timeStamp self.buildTime = timeStamp
sPos = self.verticalScrollBar().value()
theText = theText.replace("&emsp;", "&nbsp;"*4) # Refresh the tab stops
if self.mainConf.verQtValue >= 51000:
self.setTabStopDistance(self.mainConf.getTabWidth())
else:
self.setTabStopWidth(self.mainConf.getTabWidth())
theText = theText.replace("\t", "!!tab!!")
theText = theText.replace("<del>", "<span style='text-decoration: line-through;'>") theText = theText.replace("<del>", "<span style='text-decoration: line-through;'>")
theText = theText.replace("</del>", "</span>") theText = theText.replace("</del>", "</span>")
self.setHtml(theText) self.setHtml(theText)
while self.find("!!tab!!"):
theCursor = self.textCursor()
theCursor.insertText("\t")
self.verticalScrollBar().setValue(sPos)
self._updateBuildAge() self._updateBuildAge()
# Since we change the content while it may still be rendering, we mark
# the document dirty again to make sure it's re-rendered properly.
self.qDocument.markContentsDirty(0, self.qDocument.characterCount())
return return
def setStyleSheet(self, theStyles=[]): def setStyleSheet(self, theStyles=[]):
+6 -2
View File
@@ -206,8 +206,6 @@ class GuiDocEditor(QTextEdit):
# Also set the document text options for the document text flow # Also set the document text options for the document text flow
theOpt = QTextOption() theOpt = QTextOption()
if self.mainConf.verQtValue >= 51000:
theOpt.setTabStopDistance(self.mainConf.getTabWidth())
if self.mainConf.doJustify: if self.mainConf.doJustify:
theOpt.setAlignment(Qt.AlignJustify) theOpt.setAlignment(Qt.AlignJustify)
if self.mainConf.showTabsNSpaces: if self.mainConf.showTabsNSpaces:
@@ -288,6 +286,12 @@ class GuiDocEditor(QTextEdit):
self.hLight.spellCheck = spTemp self.hLight.spellCheck = spTemp
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
# Refresh the tab stops
if self.mainConf.verQtValue >= 51000:
self.setTabStopDistance(self.mainConf.getTabWidth())
else:
self.setTabStopWidth(self.mainConf.getTabWidth())
return True return True
def replaceText(self, theText): def replaceText(self, theText):
+19 -4
View File
@@ -156,7 +156,20 @@ class GuiDocViewer(QTextBrowser):
self.setText("An error occurred while generating the preview.") self.setText("An error occurred while generating the preview.")
return False return False
self.setHtml(aDoc.theResult) # Refresh the tab stops
if self.mainConf.verQtValue >= 51000:
self.setTabStopDistance(self.mainConf.getTabWidth())
else:
self.setTabStopWidth(self.mainConf.getTabWidth())
self.setHtml(aDoc.theResult.replace("\t", "!!tab!!"))
# Loop through the text and put back in the tabs. Tabs are removed by
# the setHtml function, so the ToHtml class puts in a placeholder.
while self.find("!!tab!!"):
theCursor = self.textCursor()
theCursor.insertText("\t")
if self.theHandle == tHandle: if self.theHandle == tHandle:
self.verticalScrollBar().setValue(sPos) self.verticalScrollBar().setValue(sPos)
self.theHandle = tHandle self.theHandle = tHandle
@@ -167,6 +180,10 @@ class GuiDocViewer(QTextBrowser):
# Make sure the main GUI knows we changed the content # Make sure the main GUI knows we changed the content
self.theParent.viewMeta.refreshReferences(tHandle) self.theParent.viewMeta.refreshReferences(tHandle)
# Since we change the content while it may still be rendering, we mark
# the document dirty again to make sure it's re-rendered properly.
self.qDocument.markContentsDirty(0, self.qDocument.characterCount())
return True return True
def reloadText(self): def reloadText(self):
@@ -188,7 +205,7 @@ class GuiDocViewer(QTextBrowser):
"exist, or the index is out of date. The index can be updated " "exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing F9." "from the Tools menu, or by pressing F9."
) % theTag, nwAlert.ERROR) ) % theTag, nwAlert.ERROR)
return return False
else: else:
# Let the parent handle the opening as it also ensures that # Let the parent handle the opening as it also ensures that
# the doc view panel is visible in case this request comes # the doc view panel is visible in case this request comes
@@ -357,8 +374,6 @@ class GuiDocViewer(QTextBrowser):
"}}\n" "}}\n"
".comment {{" ".comment {{"
" color: rgb({cColR},{cColG},{cColB});" " color: rgb({cColR},{cColG},{cColB});"
" margin-left: 1em;"
" margin-right: 1em;"
"}}\n" "}}\n"
".synopsis {{" ".synopsis {{"
" color: rgb({mColR},{mColG},{mColB});" " color: rgb({mColR},{mColG},{mColB});"