novelWriter 2.7.4__py3-none-any.whl → 2.7.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
novelwriter/__init__.py CHANGED
@@ -49,9 +49,9 @@ __license__ = "GPLv3"
49
49
  __author__ = "Veronica Berglyd Olsen"
50
50
  __maintainer__ = "Veronica Berglyd Olsen"
51
51
  __email__ = "code@vkbo.net"
52
- __version__ = "2.7.4"
53
- __hexversion__ = "0x020704f0"
54
- __date__ = "2025-07-15"
52
+ __version__ = "2.7.5"
53
+ __hexversion__ = "0x020705f0"
54
+ __date__ = "2025-09-14"
55
55
  __status__ = "Stable"
56
56
  __domain__ = "novelwriter.io"
57
57
 
Binary file
Binary file
Binary file
@@ -39,13 +39,14 @@ from enum import Enum, IntFlag
39
39
  from time import time
40
40
 
41
41
  from PyQt6.QtCore import (
42
- QObject, QPoint, QRegularExpression, QRunnable, Qt, QTimer, pyqtSignal,
43
- pyqtSlot
42
+ QObject, QPoint, QRect, QRegularExpression, QRunnable, Qt, QTimer,
43
+ QVariant, pyqtSignal, pyqtSlot
44
44
  )
45
45
  from PyQt6.QtGui import (
46
- QAction, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent, QKeyEvent,
47
- QKeySequence, QMouseEvent, QPalette, QPixmap, QResizeEvent, QShortcut,
48
- QTextBlock, QTextCursor, QTextDocument, QTextOption
46
+ QAction, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent,
47
+ QInputMethodEvent, QKeyEvent, QKeySequence, QMouseEvent, QPalette, QPixmap,
48
+ QResizeEvent, QShortcut, QTextBlock, QTextCursor, QTextDocument,
49
+ QTextOption
49
50
  )
50
51
  from PyQt6.QtWidgets import (
51
52
  QApplication, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
@@ -74,8 +75,9 @@ from novelwriter.text.counting import standardCounter
74
75
  from novelwriter.tools.lipsum import GuiLipsum
75
76
  from novelwriter.types import (
76
77
  QtAlignCenterTop, QtAlignJustify, QtAlignLeft, QtAlignLeftTop,
77
- QtAlignRight, QtKeepAnchor, QtModCtrl, QtModNone, QtModShift, QtMouseLeft,
78
- QtMoveAnchor, QtMoveLeft, QtMoveRight, QtScrollAlwaysOff, QtScrollAsNeeded
78
+ QtAlignRight, QtImCursorRectangle, QtKeepAnchor, QtModCtrl, QtModNone,
79
+ QtModShift, QtMouseLeft, QtMoveAnchor, QtMoveLeft, QtMoveRight,
80
+ QtScrollAlwaysOff, QtScrollAsNeeded
79
81
  )
80
82
 
81
83
  logger = logging.getLogger(__name__)
@@ -151,7 +153,7 @@ class GuiDocEditor(QPlainTextEdit):
151
153
 
152
154
  # Completer
153
155
  self._completer = CommandCompleter(self)
154
- self._completer.complete.connect(self._insertCompletion)
156
+ self._completer.insertText.connect(self._insertCompletion)
155
157
 
156
158
  # Create Custom Document
157
159
  self._qDocument = GuiTextDocument(self)
@@ -914,7 +916,7 @@ class GuiDocEditor(QPlainTextEdit):
914
916
  return True
915
917
 
916
918
  ##
917
- # Document Events and Maintenance
919
+ # Events and Overloads
918
920
  ##
919
921
 
920
922
  def keyPressEvent(self, event: QKeyEvent) -> None:
@@ -1017,6 +1019,26 @@ class GuiDocEditor(QPlainTextEdit):
1017
1019
  super().resizeEvent(event)
1018
1020
  return
1019
1021
 
1022
+ def inputMethodEvent(self, event: QInputMethodEvent) -> None:
1023
+ """Handle text being input from CJK input methods."""
1024
+ super().inputMethodEvent(event)
1025
+ if event.commitString():
1026
+ # See issues #2267 and #2517
1027
+ self.ensureCursorVisible()
1028
+ self._completerToCursor()
1029
+
1030
+ def inputMethodQuery(self, query: Qt.InputMethodQuery) -> QRect | QVariant:
1031
+ """Adjust completion windows for CJK input methods to consider
1032
+ the viewport margins.
1033
+ """
1034
+ if query == QtImCursorRectangle:
1035
+ # See issues #2267 and #2517
1036
+ vM = self.viewportMargins()
1037
+ rect = self.cursorRect()
1038
+ rect.translate(vM.left(), vM.top())
1039
+ return rect
1040
+ return super().inputMethodQuery(query)
1041
+
1020
1042
  ##
1021
1043
  # Public Slots
1022
1044
  ##
@@ -1080,24 +1102,20 @@ class GuiDocEditor(QPlainTextEdit):
1080
1102
 
1081
1103
  if (block := self._qDocument.findBlock(pos)).isValid():
1082
1104
  text = block.text()
1105
+
1083
1106
  if text and text[0] in "@%" and added + removed == 1:
1084
1107
  # Only run on single character changes, or it will trigger
1085
1108
  # at unwanted times when other changes are made to the document
1086
1109
  cursor = self.textCursor()
1087
1110
  bPos = cursor.positionInBlock()
1088
- if bPos > 0 and (viewport := self.viewport()):
1111
+ if bPos > 0:
1089
1112
  if text[0] == "@":
1090
1113
  show = self._completer.updateMetaText(text, bPos)
1091
1114
  else:
1092
1115
  show = self._completer.updateCommentText(text, bPos)
1093
1116
  if show:
1094
- point = self.cursorRect().bottomRight()
1095
- self._completer.move(viewport.mapToGlobal(point))
1096
1117
  self._completer.show()
1097
- else:
1098
- self._completer.close()
1099
- else:
1100
- self._completer.close()
1118
+ self._completerToCursor()
1101
1119
 
1102
1120
  if self._doReplace and added == 1:
1103
1121
  cursor = self.textCursor()
@@ -1121,7 +1139,7 @@ class GuiDocEditor(QPlainTextEdit):
1121
1139
  cursor.setPosition(check, QtMoveAnchor)
1122
1140
  cursor.setPosition(check + length, QtKeepAnchor)
1123
1141
  cursor.insertText(text)
1124
- self._completer.hide()
1142
+ self._completer.close()
1125
1143
  return
1126
1144
 
1127
1145
  @pyqtSlot()
@@ -1898,6 +1916,12 @@ class GuiDocEditor(QPlainTextEdit):
1898
1916
  # Internal Functions
1899
1917
  ##
1900
1918
 
1919
+ def _completerToCursor(self) -> None:
1920
+ """Make sure the completer menu is positioned by the cursor."""
1921
+ if self._completer.isVisible() and (viewport := self.viewport()):
1922
+ point = self.cursorRect().bottomLeft()
1923
+ self._completer.move(viewport.mapToGlobal(point))
1924
+
1901
1925
  def _correctWord(self, cursor: QTextCursor, word: str) -> None:
1902
1926
  """Slot for the spell check context menu triggering the
1903
1927
  replacement of a word with the word from the dictionary.
@@ -2089,10 +2113,13 @@ class CommandCompleter(QMenu):
2089
2113
  called on every keystroke on a line starting with @ or %.
2090
2114
  """
2091
2115
 
2092
- complete = pyqtSignal(int, int, str)
2116
+ __slots__ = ("_parent",)
2117
+
2118
+ insertText = pyqtSignal(int, int, str)
2093
2119
 
2094
2120
  def __init__(self, parent: QWidget) -> None:
2095
2121
  super().__init__(parent=parent)
2122
+ self._parent = parent
2096
2123
  return
2097
2124
 
2098
2125
  def updateMetaText(self, text: str, pos: int) -> bool:
@@ -2179,14 +2206,14 @@ class CommandCompleter(QMenu):
2179
2206
 
2180
2207
  def keyPressEvent(self, event: QKeyEvent) -> None:
2181
2208
  """Capture keypresses and forward most of them to the editor."""
2182
- parent = self.parent()
2183
2209
  if event.key() in (
2184
2210
  Qt.Key.Key_Up, Qt.Key.Key_Down, Qt.Key.Key_Return,
2185
2211
  Qt.Key.Key_Enter, Qt.Key.Key_Escape
2186
2212
  ):
2187
2213
  super().keyPressEvent(event)
2188
- elif isinstance(parent, GuiDocEditor):
2189
- parent.keyPressEvent(event)
2214
+ else:
2215
+ self.close() # Close to release the event lock before forwarding the key press (#2510)
2216
+ self._parent.keyPressEvent(event)
2190
2217
  return
2191
2218
 
2192
2219
  ##
@@ -2195,7 +2222,7 @@ class CommandCompleter(QMenu):
2195
2222
 
2196
2223
  def _emitComplete(self, pos: int, length: int, value: str) -> None:
2197
2224
  """Emit the signal to indicate a selection has been made."""
2198
- self.complete.emit(pos, length, value)
2225
+ self.insertText.emit(pos, length, value)
2199
2226
  return
2200
2227
 
2201
2228
 
novelwriter/types.py CHANGED
@@ -110,6 +110,8 @@ QtMoveAnchor = QTextCursor.MoveMode.MoveAnchor
110
110
  QtMoveLeft = QTextCursor.MoveOperation.Left
111
111
  QtMoveRight = QTextCursor.MoveOperation.Right
112
112
 
113
+ QtImCursorRectangle = Qt.InputMethodQuery.ImCursorRectangle
114
+
113
115
  # Size Policy
114
116
 
115
117
  QtSizeExpanding = QSizePolicy.Policy.Expanding
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: novelWriter
3
- Version: 2.7.4
3
+ Version: 2.7.5
4
4
  Summary: A plain text editor for planning and writing novels
5
5
  Author-email: Veronica Berglyd Olsen <code@vkbo.net>
6
6
  License-Expression: GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0
@@ -1,4 +1,4 @@
1
- novelwriter/__init__.py,sha256=yNt71h666LAtxC--uEHh3HQ4kOqPmnzU3z7cFK4IiD0,9566
1
+ novelwriter/__init__.py,sha256=i2yxxsiWOaTaLa7RQR3_sBc0eoCLEELdw_0wTkAeBmg,9566
2
2
  novelwriter/common.py,sha256=J4WNfcXWdeImTPhkY1vuWoVEQWTpIUE6t7Hwd3GiVzk,22243
3
3
  novelwriter/config.py,sha256=PXzcruFnFuqS4zH9RJ0hf0oUs9Vndo-i6aPOBzadsp0,44643
4
4
  novelwriter/constants.py,sha256=j_Z3cb-_wW7_cxzTdoRVR9JvmYbioLAp8Dh9TluFJSQ,28827
@@ -7,10 +7,10 @@ novelwriter/error.py,sha256=HPKDJb0NPTyVRsrPNG_I1-XfLXlvYAJuE2Xi7maWvXM,6828
7
7
  novelwriter/guimain.py,sha256=luIw3434s92wEuU4gKkerp0MQGtD_hDlCVY6ioZu0LQ,50293
8
8
  novelwriter/shared.py,sha256=8JTkFi7QXmQddGNVaSBIvKPfDXLaJ9XWNmUJblpfXvs,17795
9
9
  novelwriter/splash.py,sha256=rK71vO7NG2NETTf1vvQiWRHfkHonf2Z0xhLj_LF_wj8,2237
10
- novelwriter/types.py,sha256=OKugWeCJfxo2JHE_HGA-742nav-ucSPYRI33c9NJqsg,5170
11
- novelwriter/assets/manual.pdf,sha256=ZdrI6G1X4uum0HKndXYYj1WY1WEiA_gEsWshc8hiiEk,2130912
12
- novelwriter/assets/manual_fr.pdf,sha256=M-U1hZjw3w1yrVJ3u1LaKNehMFAmaXAGIf_I8rAvugk,2189850
13
- novelwriter/assets/sample.zip,sha256=rU4nhQalz8BY2kqVyudgTAMGuxugQKF7dC59rx3jzPU,13986
10
+ novelwriter/types.py,sha256=7RhAne83H04evhuN72KlI-O3hq-9gqsgbD2zhEgQcss,5231
11
+ novelwriter/assets/manual.pdf,sha256=nnkuryQvo3Ms22ZCUk2aGv1cZbeRqdX3-JXEgkUfScI,2131017
12
+ novelwriter/assets/manual_fr.pdf,sha256=c_gXVB6HLe7YTf-trOH6RYWVE3L8H7BMHjZAdYeQBGs,2190065
13
+ novelwriter/assets/sample.zip,sha256=wr0MuFIEMt_zEaOGUkEPAWrkutLuRUfH5HyBE0E-e7A,13982
14
14
  novelwriter/assets/i18n/nw_cs_CZ.qm,sha256=Y86wuWVB0yQBL6F5P6l-W61isSGJEkCanPa_S5eTYFc,100334
15
15
  novelwriter/assets/i18n/nw_de_DE.qm,sha256=VwyhNBVst94jlAsdsyUbzj4_ucGX6QLp16wve2jlCjg,105152
16
16
  novelwriter/assets/i18n/nw_en_US.qm,sha256=rK_f6bhwlzqLygB9xfQr9mPLDZAPmutYHr3vie-Kf5k,98000
@@ -129,7 +129,7 @@ novelwriter/formats/tomarkdown.py,sha256=CN9mJRwqoit1c9rOHwsqBZEpPjEWYvh81IVgttU
129
129
  novelwriter/formats/toodt.py,sha256=LuzN8vE46mclcfpOs9QZcElXDVOx14wjyKPkOQE6KYQ,58672
130
130
  novelwriter/formats/toqdoc.py,sha256=aJGFhq482p7lxr72bCidd2KkpkNH0mcsQvV2AcI1zgo,18315
131
131
  novelwriter/formats/toraw.py,sha256=6qLNvDc87t6c25j1H2IL9whajp1u-SOnHFk9c4gV_7E,2903
132
- novelwriter/gui/doceditor.py,sha256=yTi2w289ONN-0YJBUFSPXZXMl2tENo78u0X3HoxCAnA,117541
132
+ novelwriter/gui/doceditor.py,sha256=lcOo2AUcuLGTxLFLur2qw45vQbKxdSvLWB9lCtXoTUI,118545
133
133
  novelwriter/gui/dochighlight.py,sha256=DDCEK9iKUBd5miScrJCj9NlRj3sdOgD_bY8_4ARsSMg,19576
134
134
  novelwriter/gui/docviewer.py,sha256=7r24roJ1NcGAXC022Yo3wqdwb3AgpCkX65vN7xxQja8,34569
135
135
  novelwriter/gui/docviewerpanel.py,sha256=m5e9hO5dgV729RnOEJzNzCOU2CB3vPzp4TB4HKgTjGA,19361
@@ -154,10 +154,10 @@ novelwriter/tools/manussettings.py,sha256=pQhugOCzL957M7pqT2szajZzNgnWWA1qFiIHex
154
154
  novelwriter/tools/noveldetails.py,sha256=4BVi3Pc6HNJlYcYnyHKiIuR23uKZ9elNZOcJJOoLlAw,18067
155
155
  novelwriter/tools/welcome.py,sha256=f657EWmQxe3l2sRL3Wq1A9R5mnFciivAF5TDbsUlsRc,27431
156
156
  novelwriter/tools/writingstats.py,sha256=3U4dUlaeNR16E_Kxo5am8ov3ICH0j6S48FLJ-IptHDI,23161
157
- novelwriter-2.7.4.dist-info/licenses/LICENSE.md,sha256=2GirkkLrPfQqx7fACKRJjtKJUegKc8067erGvcDVQHM,32197
158
- novelwriter-2.7.4.dist-info/licenses/setup/LICENSE-Apache-2.0.txt,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
159
- novelwriter-2.7.4.dist-info/METADATA,sha256=2vOQNKqxIhk0cTZmFX_Lv1QcfZvtuGs_CG1u2JAUxes,2539
160
- novelwriter-2.7.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
161
- novelwriter-2.7.4.dist-info/entry_points.txt,sha256=YDUG1w361LtLsjD3YhxoTDTwM17JA5-nigjC6j5C74A,45
162
- novelwriter-2.7.4.dist-info/top_level.txt,sha256=wFFEucjEeNC_Ap5ULBuEutg5a1Uc0-YO9uFT5L2naNI,12
163
- novelwriter-2.7.4.dist-info/RECORD,,
157
+ novelwriter-2.7.5.dist-info/licenses/LICENSE.md,sha256=2GirkkLrPfQqx7fACKRJjtKJUegKc8067erGvcDVQHM,32197
158
+ novelwriter-2.7.5.dist-info/licenses/setup/LICENSE-Apache-2.0.txt,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
159
+ novelwriter-2.7.5.dist-info/METADATA,sha256=Xv_w4ptsO-vn-OQR6U6PxtrwTuHpANlXhtsvgptk3PE,2539
160
+ novelwriter-2.7.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
161
+ novelwriter-2.7.5.dist-info/entry_points.txt,sha256=YDUG1w361LtLsjD3YhxoTDTwM17JA5-nigjC6j5C74A,45
162
+ novelwriter-2.7.5.dist-info/top_level.txt,sha256=wFFEucjEeNC_Ap5ULBuEutg5a1Uc0-YO9uFT5L2naNI,12
163
+ novelwriter-2.7.5.dist-info/RECORD,,