Merge 2.7.3 release into current main

This commit is contained in:
Veronica Berglyd Olsen
2025-07-07 18:41:36 +02:00
193 changed files with 6945 additions and 2592 deletions
+4 -4
View File
@@ -28,13 +28,13 @@ import logging
from PyQt6.QtCore import pyqtSlot
from PyQt6.QtWidgets import (
QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QLabel,
QListWidget, QListWidgetItem, QVBoxLayout, QWidget
QAbstractItemView, QDialogButtonBox, QGridLayout, QLabel, QListWidget,
QListWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import SHARED
from novelwriter.extensions.configlayout import NColorLabel
from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.modified import NComboBox, NDialog
from novelwriter.extensions.switch import NSwitch
from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtUserRole
@@ -79,7 +79,7 @@ class GuiDocSplit(NDialog):
self.listBox.setMinimumWidth(400)
self.listBox.setMinimumHeight(180)
self.splitLevel = QComboBox(self)
self.splitLevel = NComboBox(self)
self.splitLevel.addItem(self.tr("Split on Heading Level 1 (Partition)"), 1)
self.splitLevel.addItem(self.tr("Split up to Heading Level 2 (Chapter)"), 2)
self.splitLevel.addItem(self.tr("Split up to Heading Level 3 (Scene)"), 3)
+43 -30
View File
@@ -35,7 +35,7 @@ from PyQt6.QtWidgets import (
from novelwriter import CONFIG, SHARED
from novelwriter.common import compact, describeFont, processDialogSymbols, uniqueCompact
from novelwriter.config import DEF_GUI, DEF_ICONS, DEF_SYNTAX, DEF_TREECOL
from novelwriter.config import DEF_GUI_DARK, DEF_GUI_LIGHT, DEF_ICONS, DEF_TREECOL
from novelwriter.constants import nwLabels, nwQuotes, nwUnicode, trConst
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.extensions.configlayout import NColorLabel, NScrollableForm
@@ -165,22 +165,34 @@ class GuiPreferences(NDialog):
)
# Colour Theme
self.guiTheme = NComboBox(self)
self.guiTheme.setMinimumWidth(200)
for theme, name in SHARED.theme.listThemes():
self.guiTheme.addItem(name, theme)
self.guiTheme.setCurrentData(CONFIG.guiTheme, DEF_GUI)
self.lightTheme = NComboBox(self)
self.lightTheme.setMinimumWidth(200)
self.darkTheme = NComboBox(self)
self.darkTheme.setMinimumWidth(200)
for key, theme in SHARED.theme.colourThemes.items():
if theme.dark:
self.darkTheme.addItem(theme.name, key)
else:
self.lightTheme.addItem(theme.name, key)
self.lightTheme.setCurrentData(CONFIG.lightTheme, DEF_GUI_LIGHT)
self.darkTheme.setCurrentData(CONFIG.darkTheme, DEF_GUI_DARK)
self.mainForm.addRow(
self.tr("Colour theme"), self.guiTheme,
self.tr("User interface colour theme."), stretch=(3, 2)
self.tr("Light colour theme"), self.lightTheme,
self.tr("You can change theme mode from the sidebar."), stretch=(3, 2)
)
self.mainForm.addRow(
self.tr("Dark colour theme"), self.darkTheme,
self.tr("You can change theme mode from the sidebar."), stretch=(3, 2)
)
# Icon Theme
self.iconTheme = NComboBox(self)
self.iconTheme.setMinimumWidth(200)
for theme, name in SHARED.theme.iconCache.listThemes():
self.iconTheme.addItem(name, theme)
for key, theme in SHARED.theme.iconCache.iconThemes.items():
self.iconTheme.addItem(theme.name, key)
self.iconTheme.setCurrentData(CONFIG.iconTheme, DEF_ICONS)
self.mainForm.addRow(
@@ -242,18 +254,6 @@ class GuiPreferences(NDialog):
self.sidebar.addButton(title, section)
self.mainForm.addGroupLabel(title, section)
# Document Colour Theme
self.guiSyntax = NComboBox(self)
self.guiSyntax.setMinimumWidth(200)
for syntax, name in SHARED.theme.listSyntax():
self.guiSyntax.addItem(name, syntax)
self.guiSyntax.setCurrentData(CONFIG.guiSyntax, DEF_SYNTAX)
self.mainForm.addRow(
self.tr("Document colour theme"), self.guiSyntax,
self.tr("Colour theme for the editor and viewer."), stretch=(3, 2)
)
# Document Font Family
self.textFont = QLineEdit(self)
self.textFont.setReadOnly(True)
@@ -294,6 +294,7 @@ class GuiPreferences(NDialog):
# Tree Icon Colours
self.iconColTree = NComboBox(self)
self.iconColTree.setMinimumWidth(200)
self.iconColTree.addItem(self.tr("Theme Colours"), DEF_TREECOL)
for key, label in nwLabels.THEME_COLORS.items():
self.iconColTree.addItem(trConst(label), key)
self.iconColTree.setCurrentData(CONFIG.iconColTree, DEF_TREECOL)
@@ -543,6 +544,13 @@ class GuiPreferences(NDialog):
unit=self.tr("px")
)
# Highlight Current Line
self.lineHighlight = NSwitch(self)
self.lineHighlight.setChecked(CONFIG.lineHighlight)
self.mainForm.addRow(
self.tr("Highlight current line"), self.lineHighlight
)
# Show Tabs and Spaces
self.showTabsNSpaces = NSwitch(self)
self.showTabsNSpaces.setChecked(CONFIG.showTabsNSpaces)
@@ -993,18 +1001,23 @@ class GuiPreferences(NDialog):
# Appearance
guiLocale = self.guiLocale.currentData()
guiTheme = self.guiTheme.currentData()
lightTheme = self.lightTheme.currentData()
darkTheme = self.darkTheme.currentData()
iconTheme = self.iconTheme.currentData()
useCharCount = self.useCharCount.isChecked()
updateTheme |= CONFIG.guiTheme != guiTheme
updateTheme |= CONFIG.lightTheme != lightTheme
updateTheme |= CONFIG.darkTheme != darkTheme
updateTheme |= CONFIG.iconTheme != iconTheme
needsRestart |= CONFIG.guiLocale != guiLocale
needsRestart |= CONFIG.guiFont != self._guiFont
refreshTree |= CONFIG.useCharCount != useCharCount
updateSyntax |= CONFIG.lightTheme != lightTheme
updateSyntax |= CONFIG.darkTheme != darkTheme
CONFIG.guiLocale = guiLocale
CONFIG.guiTheme = guiTheme
CONFIG.lightTheme = lightTheme
CONFIG.darkTheme = darkTheme
CONFIG.iconTheme = iconTheme
CONFIG.hideVScroll = self.hideVScroll.isChecked()
CONFIG.hideHScroll = self.hideHScroll.isChecked()
@@ -1013,11 +1026,6 @@ class GuiPreferences(NDialog):
CONFIG.setGuiFont(self._guiFont)
# Document Style
guiSyntax = self.guiSyntax.currentData()
updateSyntax |= CONFIG.guiSyntax != guiSyntax
CONFIG.guiSyntax = guiSyntax
CONFIG.showFullPath = self.showFullPath.isChecked()
CONFIG.incNotesWCount = self.incNotesWCount.isChecked()
CONFIG.setTextFont(self._textFont)
@@ -1058,9 +1066,14 @@ class GuiPreferences(NDialog):
CONFIG.tabWidth = self.tabWidth.value()
# Text Editing
lineHighlight = self.lineHighlight.isChecked()
updateSyntax |= CONFIG.lineHighlight != lineHighlight
CONFIG.spellLanguage = self.spellLanguage.currentData()
CONFIG.autoSelect = self.autoSelect.isChecked()
CONFIG.cursorWidth = self.cursorWidth.value()
CONFIG.lineHighlight = lineHighlight
CONFIG.showTabsNSpaces = self.showTabsNSpaces.isChecked()
CONFIG.showLineEndings = self.showLineEndings.isChecked()
+67 -21
View File
@@ -33,14 +33,14 @@ from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QCloseEvent, QColor
from PyQt6.QtWidgets import (
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
QFileDialog, QGridLayout, QHBoxLayout, QLineEdit, QMenu, QStackedWidget,
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
QFileDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
QStackedWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import formatFileFilter, qtAddAction, qtLambda, simplified
from novelwriter.constants import nwLabels, trConst
from novelwriter.core.status import NWStatus, StatusEntry
from novelwriter.core.status import CUSTOM_COL, NWStatus, StatusEntry
from novelwriter.enum import nwStatusShape
from novelwriter.extensions.configlayout import NColorLabel, NFixedPage, NScrollableForm
from novelwriter.extensions.modified import NComboBox, NDialog, NIconToolButton
@@ -326,6 +326,7 @@ class _StatusPage(NFixedPage):
self._changed = False
self._color = QColor(100, 100, 100)
self._shape = nwStatusShape.SQUARE
self._theme = CUSTOM_COL
self._icons = {}
self._iPx = SHARED.theme.baseIconHeight
@@ -384,11 +385,26 @@ class _StatusPage(NFixedPage):
self.exportButton.clicked.connect(self._exportLabels)
# Edit Form
self.labelText = QLineEdit(self)
self.labelText.setMaxLength(40)
self.labelText.setPlaceholderText(self.tr("Select item to edit"))
self.labelText.setEnabled(False)
self.labelText.textEdited.connect(self._onNameEdit)
self.editName = QLineEdit(self)
self.editName.setMaxLength(40)
self.editName.setPlaceholderText(self.tr("Select item to edit"))
self.editName.setEnabled(False)
self.editName.textEdited.connect(self._onNameEdit)
self.labelName = QLabel(self.tr("Label"), self)
self.labelName.setBuddy(self.editName)
# Icon Colours
self.iconColor = NComboBox(self)
self.iconColor.setMinimumWidth(200)
self.iconColor.setEnabled(False)
self.iconColor.addItem(self.tr("Custom"), CUSTOM_COL)
for key, label in nwLabels.THEME_COLORS.items():
self.iconColor.addItem(trConst(label), key)
self.iconColor.currentIndexChanged.connect(self._onThemeSelect)
self.labelColor = QLabel(self.tr("Colour"), self)
self.labelColor.setBuddy(self.iconColor)
buttonStyle = (
"QToolButton {padding: 0 4px;} "
@@ -425,6 +441,9 @@ class _StatusPage(NFixedPage):
self.shapeButton.setStyleSheet(buttonStyle)
self.shapeButton.setEnabled(False)
self.labelShape = QLabel(self.tr("Shape"), self)
self.labelShape.setBuddy(self.iconColor)
# Assemble
self.listControls = QVBoxLayout()
self.listControls.addWidget(self.addButton)
@@ -435,10 +454,15 @@ class _StatusPage(NFixedPage):
self.listControls.addWidget(self.importButton)
self.listControls.addWidget(self.exportButton)
self.editBox = QHBoxLayout()
self.editBox.addWidget(self.labelText, 1)
self.editBox.addWidget(self.colorButton, 0)
self.editBox.addWidget(self.shapeButton, 0)
self.editBox = QGridLayout()
self.editBox.addWidget(self.labelName, 0, 0)
self.editBox.addWidget(self.editName, 0, 1, 1, 5)
self.editBox.addWidget(self.labelColor, 1, 0)
self.editBox.addWidget(self.iconColor, 1, 1)
self.editBox.addWidget(self.colorButton, 1, 2)
self.editBox.addWidget(self.labelShape, 1, 3)
self.editBox.addWidget(self.shapeButton, 1, 4)
self.editBox.setColumnStretch(5, 1)
self.innerBox = QGridLayout()
self.innerBox.addWidget(self.listBox, 0, 0)
@@ -496,11 +520,20 @@ class _StatusPage(NFixedPage):
self._changed = True
return
@pyqtSlot(int)
def _onThemeSelect(self, index: int) -> None:
"""Update the colour handling on theme selection change."""
self._theme = str(self.iconColor.currentData())
self._setButtonIcons()
self._updateIcon()
return
@pyqtSlot()
def _onColorSelect(self) -> None:
"""Open a dialog to select the status icon colour."""
if (color := QColorDialog.getColor(self._color, self, self.trSelColor)).isValid():
self._color = color
self._theme = CUSTOM_COL
self._setButtonIcons()
self._updateIcon()
return
@@ -511,7 +544,8 @@ class _StatusPage(NFixedPage):
color = QColor(100, 100, 100)
shape = nwStatusShape.SQUARE
icon = NWStatus.createIcon(self._iPx, color, shape)
self._addItem(None, StatusEntry(self.tr("New Item"), color, shape, icon, 0))
theme = str(self.iconColor.currentData())
self._addItem(None, StatusEntry(self.tr("New Item"), color, theme, shape, icon, 0))
self._changed = True
return
@@ -537,22 +571,26 @@ class _StatusPage(NFixedPage):
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
self._color = entry.color
self._shape = entry.shape
self._theme = entry.theme
self._setButtonIcons()
self.labelText.setText(entry.name)
self.labelText.selectAll()
self.labelText.setFocus()
self.editName.setText(entry.name)
self.editName.selectAll()
self.editName.setFocus()
self.labelText.setEnabled(True)
self.editName.setEnabled(True)
self.iconColor.setEnabled(True)
self.colorButton.setEnabled(True)
self.shapeButton.setEnabled(True)
else:
self._color = QColor(100, 100, 100)
self._shape = nwStatusShape.SQUARE
self._theme = CUSTOM_COL
self._setButtonIcons()
self.labelText.setText("")
self.editName.setText("")
self.labelText.setEnabled(False)
self.editName.setEnabled(False)
self.iconColor.setEnabled(False)
self.colorButton.setEnabled(False)
self.shapeButton.setEnabled(False)
return
@@ -608,10 +646,11 @@ class _StatusPage(NFixedPage):
def _updateIcon(self) -> None:
"""Apply changes made to a status icon."""
if item := self._getSelectedItem():
icon = NWStatus.createIcon(self._iPx, self._color, self._shape)
icon = NWStatus.createIcon(self._iPx, self._pickColor(), self._shape)
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
entry.color = self._color
entry.shape = self._shape
entry.theme = self._theme
entry.icon = icon
item.setIcon(self.C_LABEL, icon)
self._changed = True
@@ -658,11 +697,18 @@ class _StatusPage(NFixedPage):
def _setButtonIcons(self) -> None:
"""Set the colour of the colour button."""
icon = NWStatus.createIcon(self._iPx, self._color, nwStatusShape.SQUARE)
icon = NWStatus.createIcon(self._iPx, self._pickColor(), nwStatusShape.SQUARE)
self.iconColor.setCurrentData(self._theme, CUSTOM_COL)
self.colorButton.setIcon(icon)
self.shapeButton.setIcon(self._icons[self._shape])
return
def _pickColor(self) -> QColor:
"""Get the correct colour value based on selections."""
if self._theme == CUSTOM_COL:
return self._color
return SHARED.theme.getBaseColor(self._theme)
class _ReplacePage(NFixedPage):