+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# novelWriter ChangeLog
|
# novelWriter Change Log
|
||||||
|
|
||||||
## Version 1.0 Release Candidate 1 [2020-11-16]
|
## Version 1.0 Release Candidate 1 [2020-11-16]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h2>Release Notes for 1.0 RC1</h2>
|
||||||
|
<p>This is the first release candidate for the upcoming release of novelWriter 1.0.</p>
|
||||||
|
<p>Since the fifth beta release about four weeks ago, not much has been changed in novelWriter. A
|
||||||
|
few minor tweaks have been made to the GUI.</p>
|
||||||
|
<p>A number of features and tools are now automatically switched off when there is no project or
|
||||||
|
document open for those features to act upon. Previously, this was a bit inconsistent, although no
|
||||||
|
serious bugs have been reported or encountered.</p>
|
||||||
|
<p>Most of the minor changes in this release should not be noticeable to most users. However, there
|
||||||
|
are a couple of noticeable changes.</p>
|
||||||
|
<h3>Typewriter Mode</h3>
|
||||||
|
<p>The "Typewriter Mode" of the editor has been improved. Essentially, this feature is a sort of
|
||||||
|
smart scroll. It tries to keep the cursor stationary in the vertical direction, and will try to
|
||||||
|
scroll the document up when the cursor skips to a new line while typing (or down in case of
|
||||||
|
backspaces). This is similar to the way a typewriter scrolls the paper when hitting the return key.
|
||||||
|
It improves the writing experience as the current active line will stay at the same eye height
|
||||||
|
level on the screen.</p>
|
||||||
|
<p>Previously, the feature would lock the cursor to a given vertical position defined by the user.
|
||||||
|
Now, instead, the cursor will remain stationary in the vertical direction at any position the user
|
||||||
|
sets it to by mouse click or keyboard navigation. The user can define a minimum distance from the
|
||||||
|
top where this feature is activated. These changes makes it more flexible in terms of where the
|
||||||
|
focus is in the editor. The feature can be controlled from the main Preferences.</p>
|
||||||
|
<h3>Switching Syntax Theme</h3>
|
||||||
|
<p>It is now possible to switch syntax highlighting theme without restarting novelWriter.
|
||||||
|
Previously, changing the theme would only half-way update the document, header and footer
|
||||||
|
background and text colours. The new settings would not be fully applied until the application was
|
||||||
|
shut down and started again, thus making it a bit tedious to look through syntax themes to find the
|
||||||
|
one you want.</p>
|
||||||
|
<p>Switching main GUI theme still requires a restart.</p>
|
||||||
|
<p><i>The full changelog is available
|
||||||
|
<a href="https://github.com/vkbo/novelWriter/blob/main/CHANGELOG.md">here</a>.</i></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -91,6 +91,7 @@ class Config:
|
|||||||
self.guiFont = "" # Defaults to system default font
|
self.guiFont = "" # Defaults to system default font
|
||||||
self.guiFontSize = 11
|
self.guiFontSize = 11
|
||||||
self.guiScale = 1.0 # Set automatically by Theme class
|
self.guiScale = 1.0 # Set automatically by Theme class
|
||||||
|
self.lastNotes = "" # The latest release notes that have been shown
|
||||||
|
|
||||||
## Sizes
|
## Sizes
|
||||||
self.winGeometry = [1200, 650]
|
self.winGeometry = [1200, 650]
|
||||||
@@ -380,6 +381,9 @@ class Config:
|
|||||||
self.guiFontSize = self._parseLine(
|
self.guiFontSize = self._parseLine(
|
||||||
cnfParse, cnfSec, "guifontsize", self.CNF_INT, self.guiFontSize
|
cnfParse, cnfSec, "guifontsize", self.CNF_INT, self.guiFontSize
|
||||||
)
|
)
|
||||||
|
self.lastNotes = self._parseLine(
|
||||||
|
cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes
|
||||||
|
)
|
||||||
|
|
||||||
## Sizes
|
## Sizes
|
||||||
cnfSec = "Sizes"
|
cnfSec = "Sizes"
|
||||||
@@ -584,6 +588,7 @@ class Config:
|
|||||||
cnfParse.set(cnfSec, "guidark", str(self.guiDark))
|
cnfParse.set(cnfSec, "guidark", str(self.guiDark))
|
||||||
cnfParse.set(cnfSec, "guifont", str(self.guiFont))
|
cnfParse.set(cnfSec, "guifont", str(self.guiFont))
|
||||||
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
|
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
|
||||||
|
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
|
||||||
|
|
||||||
## Sizes
|
## Sizes
|
||||||
cnfSec = "Sizes"
|
cnfSec = "Sizes"
|
||||||
|
|||||||
+38
-7
@@ -32,8 +32,9 @@ import os
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QCursor
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
||||||
QTextBrowser, QLabel
|
QTextBrowser, QLabel
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,6 +81,10 @@ class GuiAbout(QDialog):
|
|||||||
self.pageAbout.setOpenExternalLinks(True)
|
self.pageAbout.setOpenExternalLinks(True)
|
||||||
self.pageAbout.document().setDocumentMargin(self.mainConf.pxInt(16))
|
self.pageAbout.document().setDocumentMargin(self.mainConf.pxInt(16))
|
||||||
|
|
||||||
|
self.pageNotes = QTextBrowser()
|
||||||
|
self.pageNotes.setOpenExternalLinks(True)
|
||||||
|
self.pageNotes.document().setDocumentMargin(self.mainConf.pxInt(16))
|
||||||
|
|
||||||
self.pageLicense = QTextBrowser()
|
self.pageLicense = QTextBrowser()
|
||||||
self.pageLicense.setOpenExternalLinks(True)
|
self.pageLicense.setOpenExternalLinks(True)
|
||||||
self.pageLicense.document().setDocumentMargin(self.mainConf.pxInt(16))
|
self.pageLicense.document().setDocumentMargin(self.mainConf.pxInt(16))
|
||||||
@@ -87,6 +92,7 @@ class GuiAbout(QDialog):
|
|||||||
# Main Tab Area
|
# Main Tab Area
|
||||||
self.tabBox = QTabWidget()
|
self.tabBox = QTabWidget()
|
||||||
self.tabBox.addTab(self.pageAbout, "About")
|
self.tabBox.addTab(self.pageAbout, "About")
|
||||||
|
self.tabBox.addTab(self.pageNotes, "Release")
|
||||||
self.tabBox.addTab(self.pageLicense, "License")
|
self.tabBox.addTab(self.pageLicense, "License")
|
||||||
self.innerBox.addWidget(self.tabBox)
|
self.innerBox.addWidget(self.tabBox)
|
||||||
|
|
||||||
@@ -98,14 +104,27 @@ class GuiAbout(QDialog):
|
|||||||
self.outerBox.addWidget(self.buttonBox)
|
self.outerBox.addWidget(self.buttonBox)
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
self._setStyleSheet()
|
|
||||||
self._fillAboutPage()
|
|
||||||
self._fillLicensePage()
|
|
||||||
|
|
||||||
logger.debug("GuiAbout initialisation complete")
|
logger.debug("GuiAbout initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def populateGUI(self):
|
||||||
|
"""Populate tabs with text.
|
||||||
|
"""
|
||||||
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
|
self._setStyleSheet()
|
||||||
|
self._fillAboutPage()
|
||||||
|
self._fillNotesPage()
|
||||||
|
self._fillLicensePage()
|
||||||
|
qApp.restoreOverrideCursor()
|
||||||
|
return
|
||||||
|
|
||||||
|
def showReleaseNotes(self):
|
||||||
|
"""Show the release notes.
|
||||||
|
"""
|
||||||
|
self.tabBox.setCurrentWidget(self.pageNotes)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
@@ -192,11 +211,22 @@ class GuiAbout(QDialog):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _fillNotesPage(self):
|
||||||
|
"""Load the content for the Release Notes page.
|
||||||
|
"""
|
||||||
|
docPath = os.path.join(self.mainConf.assetPath, "text", "release_notes.htm")
|
||||||
|
if os.path.isfile(docPath):
|
||||||
|
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||||
|
helpText = inFile.read()
|
||||||
|
self.pageNotes.setHtml(helpText)
|
||||||
|
else:
|
||||||
|
self.pageNotes.setHtml("Error loading release notes text ...")
|
||||||
|
return
|
||||||
|
|
||||||
def _fillLicensePage(self):
|
def _fillLicensePage(self):
|
||||||
"""Load the content for the License page.
|
"""Load the content for the License page.
|
||||||
"""
|
"""
|
||||||
docName = "gplv3_%s.htm" % self.mainConf.guiLang
|
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
|
||||||
docPath = os.path.join(self.mainConf.assetPath, "text", docName)
|
|
||||||
if os.path.isfile(docPath):
|
if os.path.isfile(docPath):
|
||||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||||
helpText = inFile.read()
|
helpText = inFile.read()
|
||||||
@@ -221,6 +251,7 @@ class GuiAbout(QDialog):
|
|||||||
hColB = self.theParent.theTheme.colHead[2],
|
hColB = self.theParent.theTheme.colHead[2],
|
||||||
)
|
)
|
||||||
self.pageAbout.document().setDefaultStyleSheet(styleSheet)
|
self.pageAbout.document().setDefaultStyleSheet(styleSheet)
|
||||||
|
self.pageNotes.document().setDefaultStyleSheet(styleSheet)
|
||||||
self.pageLicense.document().setDefaultStyleSheet(styleSheet)
|
self.pageLicense.document().setDefaultStyleSheet(styleSheet)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
+15
-2
@@ -238,6 +238,12 @@ class GuiMain(QMainWindow):
|
|||||||
if self.mainConf.showGUI:
|
if self.mainConf.showGUI:
|
||||||
self.showProjectLoadDialog()
|
self.showProjectLoadDialog()
|
||||||
|
|
||||||
|
# Show the latest release notes, if they haven't been shown before
|
||||||
|
if self.mainConf.lastNotes != nw.__version__:
|
||||||
|
if self.mainConf.showGUI:
|
||||||
|
self.showAboutNWDialog(showNotes=True)
|
||||||
|
self.mainConf.lastNotes = nw.__version__
|
||||||
|
|
||||||
logger.debug("novelWriter is ready ...")
|
logger.debug("novelWriter is ready ...")
|
||||||
self.setStatus("novelWriter is ready ...")
|
self.setStatus("novelWriter is ready ...")
|
||||||
|
|
||||||
@@ -915,11 +921,18 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def showAboutNWDialog(self):
|
def showAboutNWDialog(self, showNotes=False):
|
||||||
"""Show the about dialog for novelWriter.
|
"""Show the about dialog for novelWriter.
|
||||||
"""
|
"""
|
||||||
dlgAbout = GuiAbout(self)
|
dlgAbout = GuiAbout(self)
|
||||||
dlgAbout.exec_()
|
dlgAbout.setModal(True)
|
||||||
|
dlgAbout.show()
|
||||||
|
qApp.processEvents()
|
||||||
|
dlgAbout.populateGUI()
|
||||||
|
|
||||||
|
if showNotes:
|
||||||
|
dlgAbout.showReleaseNotes()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def showAboutQtDialog(self):
|
def showAboutQtDialog(self):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ icons = typicons_colour_light
|
|||||||
guidark = False
|
guidark = False
|
||||||
guifont =
|
guifont =
|
||||||
guifontsize = 11
|
guifontsize = 11
|
||||||
|
lastnotes = 1.0
|
||||||
|
|
||||||
[Sizes]
|
[Sizes]
|
||||||
geometry = 1200, 650
|
geometry = 1200, 650
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ icons = typicons_colour_light
|
|||||||
guidark = True
|
guidark = True
|
||||||
guifont = Cantarell
|
guifont = Cantarell
|
||||||
guifontsize = 12
|
guifontsize = 12
|
||||||
|
lastnotes = 1.0
|
||||||
|
|
||||||
[Sizes]
|
[Sizes]
|
||||||
geometry = 1100, 650
|
geometry = 1100, 650
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def testConfigCore(tmpConf, nwTemp, nwRef):
|
|||||||
|
|
||||||
assert tmpConf.confPath == nwTemp
|
assert tmpConf.confPath == nwTemp
|
||||||
assert tmpConf.saveConfig()
|
assert tmpConf.saveConfig()
|
||||||
assert cmpFiles(testConf, refConf, [2])
|
assert cmpFiles(testConf, refConf, [2, 9])
|
||||||
assert not tmpConf.confChanged
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
assert tmpConf.loadConfig()
|
assert tmpConf.loadConfig()
|
||||||
@@ -51,7 +51,7 @@ def testConfigSetWinSize(tmpConf, nwTemp, nwRef):
|
|||||||
assert tmpConf.setWinSize(1200, 650)
|
assert tmpConf.setWinSize(1200, 650)
|
||||||
assert tmpConf.saveConfig()
|
assert tmpConf.saveConfig()
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2])
|
assert cmpFiles(testConf, refConf, [2, 9])
|
||||||
assert not tmpConf.confChanged
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -73,7 +73,7 @@ def testConfigSetTreeColWidths(tmpConf, nwTemp, nwRef):
|
|||||||
assert tmpConf.confChanged
|
assert tmpConf.confChanged
|
||||||
assert tmpConf.saveConfig()
|
assert tmpConf.saveConfig()
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2])
|
assert cmpFiles(testConf, refConf, [2, 9])
|
||||||
assert not tmpConf.confChanged
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -109,7 +109,7 @@ def testConfigSetPanePos(tmpConf, nwTemp, nwRef):
|
|||||||
assert tmpConf.confChanged
|
assert tmpConf.confChanged
|
||||||
assert tmpConf.saveConfig()
|
assert tmpConf.saveConfig()
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2])
|
assert cmpFiles(testConf, refConf, [2, 9])
|
||||||
assert not tmpConf.confChanged
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -133,7 +133,7 @@ def testConfigFlags(tmpConf, nwTemp, nwRef):
|
|||||||
assert tmpConf.confChanged
|
assert tmpConf.confChanged
|
||||||
assert tmpConf.saveConfig()
|
assert tmpConf.saveConfig()
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2])
|
assert cmpFiles(testConf, refConf, [2, 9])
|
||||||
assert not tmpConf.confChanged
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
|
|||||||
+12
-3
@@ -374,12 +374,20 @@ def testAboutBox(qtbot, monkeypatch, nwFuncTemp, nwTemp):
|
|||||||
msgAbout.show()
|
msgAbout.show()
|
||||||
|
|
||||||
assert msgAbout.pageAbout.document().characterCount() > 100
|
assert msgAbout.pageAbout.document().characterCount() > 100
|
||||||
|
assert msgAbout.pageNotes.document().characterCount() > 100
|
||||||
assert msgAbout.pageLicense.document().characterCount() > 100
|
assert msgAbout.pageLicense.document().characterCount() > 100
|
||||||
|
|
||||||
msgAbout.mainConf.guiLang = "whatever"
|
msgAbout.mainConf.assetPath = "whatever"
|
||||||
|
|
||||||
|
msgAbout._fillNotesPage()
|
||||||
|
assert msgAbout.pageNotes.toPlainText() == "Error loading release notes text ..."
|
||||||
|
|
||||||
msgAbout._fillLicensePage()
|
msgAbout._fillLicensePage()
|
||||||
assert msgAbout.pageLicense.toPlainText() == "Error loading license text ..."
|
assert msgAbout.pageLicense.toPlainText() == "Error loading license text ..."
|
||||||
|
|
||||||
|
msgAbout.showReleaseNotes()
|
||||||
|
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes
|
||||||
|
|
||||||
# Qt About
|
# Qt About
|
||||||
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *args, **kwargs: None)
|
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *args, **kwargs: None)
|
||||||
nwGUI.mainMenu.aAboutQt.activate(QAction.Trigger)
|
nwGUI.mainMenu.aAboutQt.activate(QAction.Trigger)
|
||||||
@@ -1201,8 +1209,9 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
|
|||||||
copyfile(projConf, testConf)
|
copyfile(projConf, testConf)
|
||||||
ignoreLines = [
|
ignoreLines = [
|
||||||
2, # Timestamp
|
2, # Timestamp
|
||||||
11, 12, 13, 14, 15, 16, 17, # Window sizes
|
9, # Release Notes
|
||||||
7, 27, # Fonts (depends on system default)
|
12, 13, 14, 15, 16, 17, 18, # Window sizes
|
||||||
|
7, 28, # Fonts (depends on system default)
|
||||||
]
|
]
|
||||||
assert cmpFiles(testConf, refConf, ignoreLines)
|
assert cmpFiles(testConf, refConf, ignoreLines)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user