Merge branch 'main' into unicode_fix

This commit is contained in:
Veronica Berglyd Olsen
2025-07-05 14:17:16 +02:00
170 changed files with 5273 additions and 2593 deletions
+1
View File
@@ -607,6 +607,7 @@ class ProjectBuilder:
project.data.setSaveCount(0)
project.data.setAutoCount(0)
project.data.setEditTime(0)
project.index.rebuild()
project.saveProject()
project.closeProject()
return
+4 -4
View File
@@ -363,13 +363,13 @@ class NWItem:
"""
if self.isFileType():
key = "checked" if self._active else "unchecked"
color = "green" if self._active else "red"
color = "active" if self._active else "inactive"
text = trConst(nwLabels.ACTIVE_NAME[key])
icon = SHARED.theme.getIcon(key, color)
else:
key = "noncheckable"
color = "disabled"
text = ""
icon = SHARED.theme.getIcon("noncheckable", "faded")
return text, icon
return text, SHARED.theme.getIcon(key, color)
##
# Checker Methods
+14 -8
View File
@@ -483,14 +483,14 @@ class NWProject:
def setDefaultStatusImport(self) -> None:
"""Set the default status and importance values."""
self._data.itemStatus.add(None, self.tr("New"), (120, 120, 120), "STAR", 0)
self._data.itemStatus.add(None, self.tr("Note"), (205, 171, 143), "TRIANGLE", 0)
self._data.itemStatus.add(None, self.tr("Draft"), (143, 240, 164), "CIRCLE_T", 0)
self._data.itemStatus.add(None, self.tr("Finished"), (249, 240, 107), "STAR", 0)
self._data.itemImport.add(None, self.tr("New"), (120, 120, 120), "SQUARE", 0)
self._data.itemImport.add(None, self.tr("Minor"), (220, 138, 221), "BLOCK_2", 0)
self._data.itemImport.add(None, self.tr("Major"), (220, 138, 221), "BLOCK_3", 0)
self._data.itemImport.add(None, self.tr("Main"), (220, 138, 221), "BLOCK_4", 0)
self._data.itemStatus.add(None, self.tr("New"), "faded", "STAR", 0)
self._data.itemStatus.add(None, self.tr("Note"), "red", "TRIANGLE", 0)
self._data.itemStatus.add(None, self.tr("Draft"), "yellow", "CIRCLE_T", 0)
self._data.itemStatus.add(None, self.tr("Finished"), "green", "STAR", 0)
self._data.itemImport.add(None, self.tr("New"), "purple", "SQUARE", 0)
self._data.itemImport.add(None, self.tr("Minor"), "purple", "BLOCK_2", 0)
self._data.itemImport.add(None, self.tr("Major"), "purple", "BLOCK_3", 0)
self._data.itemImport.add(None, self.tr("Main"), "purple", "BLOCK_4", 0)
return
def setProjectLang(self, language: str | None) -> None:
@@ -547,6 +547,12 @@ class NWProject:
self._tree.refreshAllItems()
return
def updateTheme(self) -> None:
"""Update theme elements."""
self._data.itemStatus.refreshIcons()
self._data.itemImport.refreshIcons()
return
def localLookup(self, word: str | int) -> str:
"""Look up a word or number in the translation map for the
project and return it. The variable is cast to a string before
+10 -5
View File
@@ -46,7 +46,7 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
FILE_VERSION = "1.5" # The current project file format version
FILE_REVISION = "5" # The current project file format revision
FILE_REVISION = "6" # The current project file format revision
HEX_VERSION = 0x0105
NUM_VERSION = {
@@ -111,6 +111,8 @@ class ProjectXMLReader:
nodes. 2.5.
Rev 5: Added novelChars and notesChars attributes to content
node. 2.7 RC 1.
Rev 6: Replaced red, green and blue attributes with a single
color attribute. 2.8 Beta 1.
"""
def __init__(self, path: str | Path) -> None:
@@ -439,12 +441,15 @@ class ProjectXMLReader:
for xEntry in xItem:
if xEntry.tag == "entry":
key = xEntry.attrib.get("key", None)
red = checkInt(xEntry.attrib.get("red", 0), 0)
green = checkInt(xEntry.attrib.get("green", 0), 0)
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
red = checkInt(xEntry.attrib.get("red", 0), 0) # Deprecated in 1.5 R6
green = checkInt(xEntry.attrib.get("green", 0), 0) # Deprecated in 1.5 R6
blue = checkInt(xEntry.attrib.get("blue", 0), 0) # Deprecated in 1.5 R6
color = xEntry.attrib.get("color") # Added in 1.5 R6
count = checkInt(xEntry.attrib.get("count", 0), 0)
shape = xEntry.attrib.get("shape", "")
sObject.add(key, xEntry.text or "", (red, green, blue), shape, count)
if color is None:
color = f"{red}, {green}, {blue}"
sObject.add(key, xEntry.text or "", color, shape, count)
return
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
+23 -12
View File
@@ -35,6 +35,7 @@ from PyQt6.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygon
from novelwriter import SHARED
from novelwriter.common import simplified
from novelwriter.constants import nwLabels
from novelwriter.enum import nwStatusShape
from novelwriter.types import QtPaintAntiAlias, QtTransparent
@@ -43,12 +44,15 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
CUSTOM_COL = "custom"
@dataclasses.dataclass
class StatusEntry:
name: str
color: QColor
theme: str
shape: nwStatusShape
icon: QIcon
count: int = 0
@@ -62,7 +66,7 @@ class StatusEntry:
return status
NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
NO_ENTRY = StatusEntry("", QColor(0, 0, 0), CUSTOM_COL, nwStatusShape.SQUARE, QIcon(), 0)
T_UpdateEntry = list[tuple[str | None, StatusEntry]]
T_StatusKind = Literal["s", "i"]
@@ -97,15 +101,12 @@ class NWStatus:
# Methods
##
def add(self, key: str | None, name: str, color: tuple[int, int, int],
shape: str, count: int) -> str:
def add(self, key: str | None, name: str, color: str, shape: str, count: int) -> str:
"""Add or update a status entry. If the key is invalid, a new
key is generated.
"""
if isinstance(color, tuple) and len(color) == 3:
qColor = QColor(*color)
else:
qColor = QColor(100, 100, 100)
qColor = SHARED.theme.parseColor(color)
theme = color if color in nwLabels.THEME_COLORS else CUSTOM_COL
try:
iShape = nwStatusShape[shape]
@@ -115,7 +116,7 @@ class NWStatus:
key = self._checkKey(key)
name = simplified(name)
icon = self.createIcon(self._height, qColor, iShape)
self._store[key] = StatusEntry(name, qColor, iShape, icon, count)
self._store[key] = StatusEntry(name, qColor, theme, iShape, icon, count)
if self._default is None:
self._default = key
@@ -157,12 +158,14 @@ class NWStatus:
def pack(self) -> Iterable[tuple[str, dict]]:
"""Pack the status entries into a dictionary."""
for key, entry in self._store.items():
if entry.theme == CUSTOM_COL:
color = entry.color.name(QColor.NameFormat.HexRgb)
else:
color = entry.theme
yield (entry.name, {
"key": key,
"count": str(entry.count),
"red": str(entry.color.red()),
"green": str(entry.color.green()),
"blue": str(entry.color.blue()),
"color": color,
"shape": entry.shape.name,
})
return
@@ -179,12 +182,20 @@ class NWStatus:
try:
shape = nwStatusShape[str(data[0])]
color = QColor(str(data[1]))
theme = CUSTOM_COL if data[1].startswith("#") else data[1]
icon = NWStatus.createIcon(self._height, color, shape)
return StatusEntry(simplified(data[2]), color, shape, icon)
return StatusEntry(simplified(data[2]), color, theme, shape, icon)
except Exception:
logger.error("Could not parse entry %s", str(data))
return None
def refreshIcons(self) -> None:
"""Refresh all icons."""
for entry in self._store.values():
entry.color = SHARED.theme.parseColor(entry.theme)
entry.icon = NWStatus.createIcon(self._height, entry.color, entry.shape)
return
@staticmethod
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
"""Generate an icon for a status label."""