diff --git a/.gitignore b/.gitignore
index 5d442ef0..9260acd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
*.egg-info
setup.iss
novelwriter.desktop
+novelWriter.pyw
i18n/*.qm
i18n/*.qph
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9af3972d..8f3a0e73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,39 @@
# novelWriter Changelog
+## Version 1.2.2 [2021-03-28]
+
+### Release Notes
+
+This patch release is a bug fix release addressing some inconsistencies and issues with the
+document header buttons when Focus Mode is active. The keyboard shortcuts for search and replace
+should now also work in Focus Mode. In addition, the setup script for novelWriter has been improved
+when installing on Windows.
+
+### Detailed Changelog
+
+**Bugfixes**
+
+* The way Focus Mode worked when activated through the menu and through the document header button
+ were inconsistent. The header button would deactivate the edit, search and close buttons, while
+ the menu entry would not. These two methods now call the same set of functions to ensure the
+ behaviour is consistent. PR #717.
+* Closing the document while in Focus Mode now ends Focus Mode. Previously, the editor would be
+ left stuck in Focus Mode with no way to exit. PR #717.
+
+**User Interface**
+
+* The keyboard shortcuts for the search and replace tool now also work in Focus Mode. Previously,
+ the menu entries and their shortcuts were deactivated in this mode. Issue #716. PR #717.
+
+**Installation**
+
+* The setup script command do build minimal install archive files now also generate SHA 256 sum
+ files. PR #724.
+* The setup script will now copy the `novelWriter.py` file to `novelWriter.pyw` if it doesn't exist
+ when the `win-install` command is run. Issue #727. PR #728.
+
+----
+
## Version 1.2.1 [2021-03-21]
### Release Notes
diff --git a/nw/assets/text/release_notes.htm b/nw/assets/text/release_notes.htm
index c610131c..864521b1 100644
--- a/nw/assets/text/release_notes.htm
+++ b/nw/assets/text/release_notes.htm
@@ -3,6 +3,17 @@
+Release Notes for 1.2.2
+Released on 28 March 2021
+This patch release is a bug fix release addressing some inconsistencies and issues with the
+document header buttons when Focus Mode is active. The keyboard shortcuts for search and replace
+should now also work in Focus Mode. In addition, the setup script for novelWriter has been improved
+when installing on Windows.
+
+See also the Releases page.
+
+
+
Release Notes for 1.2.1
Released on 21 March 2021
This patch release is a bug fix release addressing issues with the document editor's search and
@@ -10,8 +21,6 @@ replace tool. Due to some recently added restrictions on when various tools are
on which part of the main window has the user's focus, the search tool keyboard shortcuts and
buttons were blocked when they shouldn't. This release resolves these issues.
-See also the Releases page.
-
Release Notes for 1.2
diff --git a/nw/constants/__init__.py b/nw/constants/__init__.py
index 0aeb0ae4..f0f12c60 100644
--- a/nw/constants/__init__.py
+++ b/nw/constants/__init__.py
@@ -5,7 +5,7 @@ from nw.constants.constants import (
)
from nw.constants.enum import (
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
- nwDocInsert
+ nwDocInsert, nwWidget
)
__all__ = [
@@ -26,4 +26,5 @@ __all__ = [
"nwItemType",
"nwOutline",
"nwDocInsert",
+ "nwWidget",
]
diff --git a/nw/constants/enum.py b/nw/constants/enum.py
index 4e800666..7136377d 100644
--- a/nw/constants/enum.py
+++ b/nw/constants/enum.py
@@ -112,6 +112,15 @@ class nwAlert(Enum):
# END Enum nwAlert
+class nwWidget(Enum):
+
+ TREE = 1
+ EDITOR = 2
+ VIEWER = 3
+ OUTLINE = 4
+
+# END Enum nwWidget
+
class nwOutline(Enum):
TITLE = 0
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index 92666b3e..e0aee4d3 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -2223,7 +2223,6 @@ class GuiDocEditHeader(QWidget):
fPx = int(0.9*self.theTheme.fontPixelSize)
hSp = self.mainConf.pxInt(6)
- self.buttonSize = fPx + hSp
# Main Widget Settings
self.setAutoFillBackground(True)
@@ -2369,6 +2368,17 @@ class GuiDocEditHeader(QWidget):
return True
+ def updateFocusMode(self):
+ """Update the minimise/maximise icon of the Focus Mode button.
+ This function is called by the GuiMain class via the
+ toggleFocusMode function and should not be activated directly.
+ """
+ if self.theParent.isFocusMode:
+ self.minmaxButton.setIcon(self.theTheme.getIcon("minimise"))
+ else:
+ self.minmaxButton.setIcon(self.theTheme.getIcon("maximise"))
+ return
+
##
# Slots
##
@@ -2399,18 +2409,6 @@ class GuiDocEditHeader(QWidget):
"""Switch on or off Focus Mode.
"""
self.theParent.toggleFocusMode()
- 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(0, 0, 0, 0)
- self.editButton.setVisible(True)
- self.searchButton.setVisible(True)
- self.closeButton.setVisible(True)
return
##
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 6bf3a25f..3f2579fd 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -33,7 +33,7 @@ from PyQt5.QtWidgets import QMenuBar, QAction
from nw.constants import (
trConst, nwItemType, nwItemClass, nwDocAction, nwDocInsert, nwKeyWords,
- nwLabels, nwUnicode
+ nwLabels, nwUnicode, nwWidget
)
logger = logging.getLogger(__name__)
@@ -471,28 +471,28 @@ class GuiMainMenu(QMenuBar):
self.aFocusTree = QAction(self.tr("Focus Project Tree"), self)
self.aFocusTree.setStatusTip(self.tr("Move focus to project tree"))
self.aFocusTree.setShortcut("Alt+1")
- self.aFocusTree.triggered.connect(lambda: self.theParent.setFocus(1))
+ self.aFocusTree.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.TREE))
self.viewMenu.addAction(self.aFocusTree)
# View > Document Pane 1
self.aFocusEditor = QAction(self.tr("Focus Document Editor"), self)
self.aFocusEditor.setStatusTip(self.tr("Move focus to left document pane"))
self.aFocusEditor.setShortcut("Alt+2")
- self.aFocusEditor.triggered.connect(lambda: self.theParent.setFocus(2))
+ self.aFocusEditor.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.EDITOR))
self.viewMenu.addAction(self.aFocusEditor)
# View > Document Pane 2
self.aFocusView = QAction(self.tr("Focus Document Viewer"), self)
self.aFocusView.setStatusTip(self.tr("Move focus to right document pane"))
self.aFocusView.setShortcut("Alt+3")
- self.aFocusView.triggered.connect(lambda: self.theParent.setFocus(3))
+ self.aFocusView.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.VIEWER))
self.viewMenu.addAction(self.aFocusView)
# View > Outline
self.aFocusOutline = QAction(self.tr("Focus Outline"), self)
self.aFocusOutline.setStatusTip(self.tr("Move focus to outline"))
self.aFocusOutline.setShortcut("Alt+4")
- self.aFocusOutline.triggered.connect(lambda: self.theParent.setFocus(4))
+ self.aFocusOutline.triggered.connect(lambda: self.theParent.switchFocus(nwWidget.OUTLINE))
self.viewMenu.addAction(self.aFocusOutline)
# View > Separator
diff --git a/nw/guimain.py b/nw/guimain.py
index f3ad2c13..d6da8940 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -46,7 +46,7 @@ from nw.gui import (
GuiProjectTree, GuiProjectWizard, GuiTheme, GuiWordList, GuiWritingStats
)
from nw.core import NWProject, NWDoc, NWIndex
-from nw.constants import nwItemType, nwItemClass, nwAlert, nwLists
+from nw.constants import nwItemType, nwItemClass, nwAlert, nwLists, nwWidget
from nw.common import getGuiItem, hexToInt
logger = logging.getLogger(__name__)
@@ -567,6 +567,10 @@ class GuiMain(QMainWindow):
logger.error("No project open")
return False
+ # Disable focus mode if it is active
+ if self.isFocusMode:
+ self.toggleFocusMode()
+
self.docEditor.saveCursorPosition()
if self.docEditor.docChanged:
self.saveDocument()
@@ -813,10 +817,10 @@ class GuiMain(QMainWindow):
return False
if tHandle is None:
- if self.treeView.hasFocus():
- tHandle = self.treeView.getSelectedHandle()
- elif self.docEditor.hasFocus():
+ if self.docEditor.anyFocus() or self.isFocusMode:
tHandle = self.docEditor.theHandle
+ else:
+ tHandle = self.treeView.getSelectedHandle()
if tHandle is None:
logger.warning("No item selected")
@@ -1176,18 +1180,18 @@ class GuiMain(QMainWindow):
return True
- def setFocus(self, paneNo):
+ def switchFocus(self, paneNo):
"""Switch focus between main GUI views.
"""
- if paneNo == 1:
+ if paneNo == nwWidget.TREE:
self.treeView.setFocus()
- elif paneNo == 2:
+ elif paneNo == nwWidget.EDITOR:
self.mainTabs.setCurrentWidget(self.splitDocs)
self.docEditor.setFocus()
- elif paneNo == 3:
+ elif paneNo == nwWidget.VIEWER:
self.mainTabs.setCurrentWidget(self.splitDocs)
self.docViewer.setFocus()
- elif paneNo == 4:
+ elif paneNo == nwWidget.OUTLINE:
self.mainTabs.setCurrentWidget(self.splitOutline)
self.projView.setFocus()
return
@@ -1224,6 +1228,7 @@ class GuiMain(QMainWindow):
if self.isFocusMode:
logger.debug("Activating Focus Mode")
self.mainTabs.setCurrentWidget(self.splitDocs)
+ self.switchFocus(nwWidget.EDITOR)
else:
logger.debug("Deactivating Focus Mode")
@@ -1235,6 +1240,7 @@ class GuiMain(QMainWindow):
hideDocFooter = self.isFocusMode and self.mainConf.hideFocusFooter
self.docEditor.docFooter.setVisible(not hideDocFooter)
+ self.docEditor.docHeader.updateFocusMode()
if self.splitView.isVisible():
self.splitView.setVisible(False)
@@ -1272,11 +1278,12 @@ class GuiMain(QMainWindow):
"""
# Project
self.addAction(self.mainMenu.aSaveProject)
+ self.addAction(self.mainMenu.aEditItem)
self.addAction(self.mainMenu.aExitNW)
# Document
self.addAction(self.mainMenu.aSaveDoc)
- self.addAction(self.mainMenu.aFileDetails)
+ self.addAction(self.mainMenu.aCloseDoc)
# Edit
self.addAction(self.mainMenu.aEditUndo)
@@ -1320,6 +1327,13 @@ class GuiMain(QMainWindow):
for mAction, _ in self.mainMenu.mInsKWItems.values():
self.addAction(mAction)
+ # Search
+ self.addAction(self.mainMenu.aFind)
+ self.addAction(self.mainMenu.aReplace)
+ self.addAction(self.mainMenu.aFindNext)
+ self.addAction(self.mainMenu.aFindPrev)
+ self.addAction(self.mainMenu.aReplaceNext)
+
# Format
self.addAction(self.mainMenu.aFmtEmph)
self.addAction(self.mainMenu.aFmtStrong)
diff --git a/setup.py b/setup.py
index f11b5014..dc47aca3 100755
--- a/setup.py
+++ b/setup.py
@@ -291,7 +291,8 @@ def makeMinimalPackage(targetOS):
else:
targName = ""
- outFile = os.path.join("dist", f"novelWriter-{__version__}-minimal{targName}.zip")
+ zipFile = f"novelWriter-{__version__}-minimal{targName}.zip"
+ outFile = os.path.join("dist", zipFile)
if os.path.isfile(outFile):
os.unlink(outFile)
@@ -332,7 +333,17 @@ def makeMinimalPackage(targetOS):
zipObj.write(aFile)
print("")
- print("Built file: %s" % outFile)
+ print("Created File: %s" % outFile)
+
+ try:
+ shaFile = open(outFile+".sha256", mode="w")
+ subprocess.call(["sha256sum", zipFile], stdout=shaFile, cwd="dist")
+ shaFile.close()
+ print("SHA256 Sum: %s" % (outFile+".sha256"))
+ except Exception as e:
+ print("Could not generate sha256 file")
+ print(str(e))
+
print("")
return
@@ -710,6 +721,9 @@ def winInstall():
targetPy = os.path.join(targetDir, "novelWriter.pyw")
targetIcon = os.path.join(targetDir, "nw", "assets", "icons", "novelwriter.ico")
+ if not os.path.isfile(targetPy):
+ shutil.copy2(os.path.join(targetDir, "novelWriter.py"), targetPy)
+
print("Collecting Info ...")
print("Desktop Folder: %s" % desktopDir)
print("Start Menu Folder: %s" % startMenuDir)
diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py
index dfcf1f86..b53da662 100644
--- a/tests/test_gui/test_gui_doceditor.py
+++ b/tests/test_gui/test_gui_doceditor.py
@@ -33,7 +33,7 @@ from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
from nw.gui.itemeditor import GuiItemEditor
from nw.gui.doceditor import GuiDocEditor
from nw.gui.projtree import GuiProjectTree
-from nw.constants import nwItemType, nwDocAction
+from nw.constants import nwItemType, nwDocAction, nwWidget
keyDelay = 2
typeDelay = 1
@@ -117,14 +117,14 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
nwGUI.mainConf.autoScroll = True
# Add a Character File
- nwGUI.setFocus(1)
+ nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
assert nwGUI.openSelectedItem()
# Type something into the document
- nwGUI.setFocus(2)
+ nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
for c in "# Jane Doe":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
@@ -139,14 +139,14 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
# Add a Plot File
- nwGUI.setFocus(1)
+ nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("44cb730c42048").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
assert nwGUI.openSelectedItem()
# Type something into the document
- nwGUI.setFocus(2)
+ nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
for c in "# Main Plot":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
@@ -161,7 +161,7 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
# Add a World File
- nwGUI.setFocus(1)
+ nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("811786ad1ae74").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
@@ -173,7 +173,7 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
nwGUI.docEditor.replaceText("")
# Type something into the document
- nwGUI.setFocus(2)
+ nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
for c in "# Main Location":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
@@ -192,7 +192,7 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
nwGUI._autoSaveProject()
# Select the 'New Scene' file
- nwGUI.setFocus(1)
+ nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("73475cb40a568").setExpanded(True)
nwGUI.treeView._getTreeItem("31489056e0916").setExpanded(True)
@@ -200,7 +200,7 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
assert nwGUI.openSelectedItem()
# Type something into the document
- nwGUI.setFocus(2)
+ nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
for c in "# Novel":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
@@ -300,7 +300,7 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
qtbot.wait(stepDelay)
# Open and view the edited document
- nwGUI.setFocus(3)
+ nwGUI.switchFocus(nwWidget.VIEWER)
assert nwGUI.openDocument("0e17daca5f3e1")
assert nwGUI.viewDocument("0e17daca5f3e1")
qtbot.wait(stepDelay)
diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py
index 3e8886a8..e2ef781a 100644
--- a/tests/test_gui/test_gui_mainmenu.py
+++ b/tests/test_gui/test_gui_mainmenu.py
@@ -28,7 +28,9 @@ from PyQt5.QtGui import QTextCursor, QTextBlock
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
from nw.gui.doceditor import GuiDocEditor
-from nw.constants import nwUnicode, nwDocAction, nwDocInsert, nwKeyWords
+from nw.constants import (
+ nwUnicode, nwDocAction, nwDocInsert, nwKeyWords, nwWidget
+)
keyDelay = 2
typeDelay = 1
@@ -366,7 +368,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None
- nwGUI.setFocus(1)
+ nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("0e17daca5f3e1").setSelected(True)
assert nwGUI.openSelectedItem()