Merge branch 'main' into dev

This commit is contained in:
Veronica Berglyd Olsen
2024-01-27 17:04:44 +01:00
23 changed files with 3219 additions and 2805 deletions
@@ -1,5 +1,6 @@
{
"Synopsis": "Synopsis",
"Short Description": "Kurzbeschreibung",
"Comment": "Kommentar",
"Notes": "Notizen",
"Tag": "Schlagwort",
@@ -1,7 +1,18 @@
{
"Synopsis": "概要",
"Short Description": "简短描述",
"Comment": "注释",
"Notes": "笔记",
"Tag": "标签",
"Point of View": "视角",
"Focus": "聚焦",
"Characters": "角色",
"Plot": "情节",
"Timeline": "时间线",
"Locations": "地点",
"Objects": "物品",
"Entities": "条目",
"Custom": "自定义",
"0": "零",
"1": "一",
"2": "二",
+10
View File
@@ -5,6 +5,8 @@
<h2>Release Notes for 2.2</h2>
<p><i>Released on 17 December 2023</i></p>
<p>Scroll down for <a href="#patch">Patch Notes</a></p>
<p>This release comes with a number of new features. These are some highlights.</p>
<p>In addition to the common Markdown style formatting for bold, italic and strike through, a set
of new shortcodes have been added. The shortcodes are far more flexible than the Markdown style
@@ -46,5 +48,13 @@ had to be removed. It may be added back later.</p>
<p><i>See also the <a href="https://github.com/vkbo/novelWriter/releases">Releases</a> page.</i></p>
<a name="patch"></a><h2>Patch Notes</h2>
<h3>Patch 2.2.1 &ndash; 27 January 2024</h3>
<p>This is a patch release that fixes an issue where the Project View would sometimes switch to the
Novel View when a new item was created. This patch also includes updated translations for German
and Chinese.</p>
</body>
</html>
+14 -14
View File
@@ -46,9 +46,9 @@ from novelwriter.constants import nwConst, nwUnicode
logger = logging.getLogger(__name__)
# =============================================================================================== #
##
# Checker Functions
# =============================================================================================== #
##
def checkStringNone(value: Any, default: str | None) -> str | None:
"""Check if a variable is a string or a None."""
@@ -131,9 +131,9 @@ def checkPath(value: Any, default: Path) -> Path:
return default
# =============================================================================================== #
##
# Validator Functions
# =============================================================================================== #
##
def isHandle(value: Any) -> bool:
"""Check if a string is a valid novelWriter handle.
@@ -204,9 +204,9 @@ def checkIntTuple(value: int, valid: tuple | list | set, default: int) -> int:
return default
# =============================================================================================== #
##
# Formatting Functions
# =============================================================================================== #
##
def formatInt(value: int) -> str:
"""Formats an integer with k, M, G etc."""
@@ -255,9 +255,9 @@ def formatVersion(value: str) -> str:
return value.lower().replace("a", " Alpha ").replace("b", " Beta ").replace("rc", " RC ")
# =============================================================================================== #
##
# String Functions
# =============================================================================================== #
##
def simplified(text: str) -> str:
"""Take a string and strip leading and trailing whitespaces, and
@@ -376,9 +376,9 @@ def numberToRoman(value: int, toLower: bool = False) -> str:
return roman.lower() if toLower else roman
# =============================================================================================== #
##
# Encoder Functions
# =============================================================================================== #
##
def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
"""Encode a dictionary, list or tuple as a json object or array, and
@@ -468,9 +468,9 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
return
# =============================================================================================== #
##
# File and File System Functions
# =============================================================================================== #
##
def readTextFile(path: str | Path) -> str:
"""Read the content of a text file in a robust manner."""
@@ -512,9 +512,9 @@ def openExternalPath(path: Path) -> bool:
return False
# =============================================================================================== #
##
# Classes
# =============================================================================================== #
##
class NWConfigParser(ConfigParser):
"""Common: Adapted Config Parser
+2 -4
View File
@@ -41,6 +41,7 @@ from PyQt5.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout
from novelwriter.common import minmax
from novelwriter.constants import nwHeaders, nwUnicode, trConst, nwLabels
from novelwriter.core.item import NWItem
@@ -49,9 +50,6 @@ from novelwriter.dialogs.docmerge import GuiDocMerge
from novelwriter.dialogs.docsplit import GuiDocSplit
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projectsettings import GuiProjectSettings
from novelwriter.enum import (
nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwWidget
)
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
@@ -682,7 +680,7 @@ class GuiProjectTree(QTreeWidget):
# Add the new item to the project tree
self.revealNewTreeItem(tHandle, nHandle=nHandle, wordCount=True)
self.mainGui.switchFocus(nwWidget.TREE)
self.projView.setTreeFocus() # See issue #1376
return True
+4 -2
View File
@@ -833,7 +833,8 @@ class GuiMain(QMainWindow):
def showBuildManuscriptDialog(self) -> None:
"""Open the build manuscript dialog."""
if SHARED.hasProject:
dialog = GuiManuscript(self)
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
dialog = GuiManuscript(self)
dialog.setModal(False)
dialog.show()
dialog.raise_()
@@ -854,7 +855,8 @@ class GuiMain(QMainWindow):
def showWritingStatsDialog(self) -> None:
"""Open the session stats dialog."""
if SHARED.hasProject:
dialog = GuiWritingStats(self)
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
dialog = GuiWritingStats(self)
dialog.setModal(False)
dialog.show()
dialog.raise_()
+10 -1
View File
@@ -27,7 +27,7 @@ from __future__ import annotations
import logging
from time import time
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, TypeVar
from pathlib import Path
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
@@ -43,6 +43,8 @@ if TYPE_CHECKING: # pragma: no cover
logger = logging.getLogger(__name__)
NWWidget = TypeVar("NWWidget", bound=QWidget)
class SharedData(QObject):
@@ -230,6 +232,13 @@ class SharedData(QObject):
)
return Path(projFile) if projFile else None
def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None:
"""Find a top level widget."""
for widget in self.mainGui.children():
if isinstance(widget, kind):
return widget
return None
##
# Signal Proxy
##