+1
-1
@@ -1,4 +1,4 @@
|
||||
# novelWriter ChangeLog
|
||||
# novelWriter Change Log
|
||||
|
||||
## 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.guiFontSize = 11
|
||||
self.guiScale = 1.0 # Set automatically by Theme class
|
||||
self.lastNotes = "" # The latest release notes that have been shown
|
||||
|
||||
## Sizes
|
||||
self.winGeometry = [1200, 650]
|
||||
@@ -380,6 +381,9 @@ class Config:
|
||||
self.guiFontSize = self._parseLine(
|
||||
cnfParse, cnfSec, "guifontsize", self.CNF_INT, self.guiFontSize
|
||||
)
|
||||
self.lastNotes = self._parseLine(
|
||||
cnfParse, cnfSec, "lastnotes", self.CNF_STR, self.lastNotes
|
||||
)
|
||||
|
||||
## Sizes
|
||||
cnfSec = "Sizes"
|
||||
@@ -584,6 +588,7 @@ class Config:
|
||||
cnfParse.set(cnfSec, "guidark", str(self.guiDark))
|
||||
cnfParse.set(cnfSec, "guifont", str(self.guiFont))
|
||||
cnfParse.set(cnfSec, "guifontsize", str(self.guiFontSize))
|
||||
cnfParse.set(cnfSec, "lastnotes", str(self.lastNotes))
|
||||
|
||||
## Sizes
|
||||
cnfSec = "Sizes"
|
||||
|
||||
+38
-7
@@ -32,8 +32,9 @@ import os
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
||||
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
||||
QTextBrowser, QLabel
|
||||
)
|
||||
|
||||
@@ -80,6 +81,10 @@ class GuiAbout(QDialog):
|
||||
self.pageAbout.setOpenExternalLinks(True)
|
||||
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.setOpenExternalLinks(True)
|
||||
self.pageLicense.document().setDocumentMargin(self.mainConf.pxInt(16))
|
||||
@@ -87,6 +92,7 @@ class GuiAbout(QDialog):
|
||||
# Main Tab Area
|
||||
self.tabBox = QTabWidget()
|
||||
self.tabBox.addTab(self.pageAbout, "About")
|
||||
self.tabBox.addTab(self.pageNotes, "Release")
|
||||
self.tabBox.addTab(self.pageLicense, "License")
|
||||
self.innerBox.addWidget(self.tabBox)
|
||||
|
||||
@@ -98,14 +104,27 @@ class GuiAbout(QDialog):
|
||||
self.outerBox.addWidget(self.buttonBox)
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
self._setStyleSheet()
|
||||
self._fillAboutPage()
|
||||
self._fillLicensePage()
|
||||
|
||||
logger.debug("GuiAbout initialisation complete")
|
||||
|
||||
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
|
||||
##
|
||||
@@ -192,11 +211,22 @@ class GuiAbout(QDialog):
|
||||
|
||||
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):
|
||||
"""Load the content for the License page.
|
||||
"""
|
||||
docName = "gplv3_%s.htm" % self.mainConf.guiLang
|
||||
docPath = os.path.join(self.mainConf.assetPath, "text", docName)
|
||||
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
|
||||
if os.path.isfile(docPath):
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
helpText = inFile.read()
|
||||
@@ -221,6 +251,7 @@ class GuiAbout(QDialog):
|
||||
hColB = self.theParent.theTheme.colHead[2],
|
||||
)
|
||||
self.pageAbout.document().setDefaultStyleSheet(styleSheet)
|
||||
self.pageNotes.document().setDefaultStyleSheet(styleSheet)
|
||||
self.pageLicense.document().setDefaultStyleSheet(styleSheet)
|
||||
|
||||
return
|
||||
|
||||
+15
-2
@@ -238,6 +238,12 @@ class GuiMain(QMainWindow):
|
||||
if self.mainConf.showGUI:
|
||||
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 ...")
|
||||
self.setStatus("novelWriter is ready ...")
|
||||
|
||||
@@ -915,11 +921,18 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return
|
||||
|
||||
def showAboutNWDialog(self):
|
||||
def showAboutNWDialog(self, showNotes=False):
|
||||
"""Show the about dialog for novelWriter.
|
||||
"""
|
||||
dlgAbout = GuiAbout(self)
|
||||
dlgAbout.exec_()
|
||||
dlgAbout.setModal(True)
|
||||
dlgAbout.show()
|
||||
qApp.processEvents()
|
||||
dlgAbout.populateGUI()
|
||||
|
||||
if showNotes:
|
||||
dlgAbout.showReleaseNotes()
|
||||
|
||||
return
|
||||
|
||||
def showAboutQtDialog(self):
|
||||
|
||||
@@ -6,6 +6,7 @@ icons = typicons_colour_light
|
||||
guidark = False
|
||||
guifont =
|
||||
guifontsize = 11
|
||||
lastnotes = 1.0
|
||||
|
||||
[Sizes]
|
||||
geometry = 1200, 650
|
||||
|
||||
@@ -6,6 +6,7 @@ icons = typicons_colour_light
|
||||
guidark = True
|
||||
guifont = Cantarell
|
||||
guifontsize = 12
|
||||
lastnotes = 1.0
|
||||
|
||||
[Sizes]
|
||||
geometry = 1100, 650
|
||||
|
||||
@@ -14,7 +14,7 @@ def testConfigCore(tmpConf, nwTemp, nwRef):
|
||||
|
||||
assert tmpConf.confPath == nwTemp
|
||||
assert tmpConf.saveConfig()
|
||||
assert cmpFiles(testConf, refConf, [2])
|
||||
assert cmpFiles(testConf, refConf, [2, 9])
|
||||
assert not tmpConf.confChanged
|
||||
|
||||
assert tmpConf.loadConfig()
|
||||
@@ -51,7 +51,7 @@ def testConfigSetWinSize(tmpConf, nwTemp, nwRef):
|
||||
assert tmpConf.setWinSize(1200, 650)
|
||||
assert tmpConf.saveConfig()
|
||||
|
||||
assert cmpFiles(testConf, refConf, [2])
|
||||
assert cmpFiles(testConf, refConf, [2, 9])
|
||||
assert not tmpConf.confChanged
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -73,7 +73,7 @@ def testConfigSetTreeColWidths(tmpConf, nwTemp, nwRef):
|
||||
assert tmpConf.confChanged
|
||||
assert tmpConf.saveConfig()
|
||||
|
||||
assert cmpFiles(testConf, refConf, [2])
|
||||
assert cmpFiles(testConf, refConf, [2, 9])
|
||||
assert not tmpConf.confChanged
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -109,7 +109,7 @@ def testConfigSetPanePos(tmpConf, nwTemp, nwRef):
|
||||
assert tmpConf.confChanged
|
||||
assert tmpConf.saveConfig()
|
||||
|
||||
assert cmpFiles(testConf, refConf, [2])
|
||||
assert cmpFiles(testConf, refConf, [2, 9])
|
||||
assert not tmpConf.confChanged
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -133,7 +133,7 @@ def testConfigFlags(tmpConf, nwTemp, nwRef):
|
||||
assert tmpConf.confChanged
|
||||
assert tmpConf.saveConfig()
|
||||
|
||||
assert cmpFiles(testConf, refConf, [2])
|
||||
assert cmpFiles(testConf, refConf, [2, 9])
|
||||
assert not tmpConf.confChanged
|
||||
|
||||
@pytest.mark.core
|
||||
|
||||
+12
-3
@@ -374,12 +374,20 @@ def testAboutBox(qtbot, monkeypatch, nwFuncTemp, nwTemp):
|
||||
msgAbout.show()
|
||||
|
||||
assert msgAbout.pageAbout.document().characterCount() > 100
|
||||
assert msgAbout.pageNotes.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()
|
||||
assert msgAbout.pageLicense.toPlainText() == "Error loading license text ..."
|
||||
|
||||
msgAbout.showReleaseNotes()
|
||||
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes
|
||||
|
||||
# Qt About
|
||||
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *args, **kwargs: None)
|
||||
nwGUI.mainMenu.aAboutQt.activate(QAction.Trigger)
|
||||
@@ -1201,8 +1209,9 @@ def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp, nwRef, tmpC
|
||||
copyfile(projConf, testConf)
|
||||
ignoreLines = [
|
||||
2, # Timestamp
|
||||
11, 12, 13, 14, 15, 16, 17, # Window sizes
|
||||
7, 27, # Fonts (depends on system default)
|
||||
9, # Release Notes
|
||||
12, 13, 14, 15, 16, 17, 18, # Window sizes
|
||||
7, 28, # Fonts (depends on system default)
|
||||
]
|
||||
assert cmpFiles(testConf, refConf, ignoreLines)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user