novelWriter 2.6.2__py3-none-any.whl → 2.7b1__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.
Files changed (334) hide show
  1. novelwriter/__init__.py +84 -74
  2. novelwriter/assets/i18n/nw_cs_CZ.qm +0 -0
  3. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  4. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  5. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  6. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  7. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  8. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  9. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  10. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  11. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  12. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  13. novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
  14. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  15. novelwriter/assets/i18n/project_en_GB.json +1 -0
  16. novelwriter/assets/icons/font_awesome.icons +109 -0
  17. novelwriter/assets/icons/material_filled_bold.icons +109 -0
  18. novelwriter/assets/icons/material_filled_normal.icons +109 -0
  19. novelwriter/assets/icons/material_filled_thin.icons +109 -0
  20. novelwriter/assets/icons/material_rounded_bold.icons +109 -0
  21. novelwriter/assets/icons/material_rounded_normal.icons +109 -0
  22. novelwriter/assets/icons/material_rounded_thin.icons +109 -0
  23. novelwriter/assets/icons/remix_filled.icons +108 -0
  24. novelwriter/assets/icons/remix_outline.icons +108 -0
  25. novelwriter/assets/manual.pdf +0 -0
  26. novelwriter/assets/{manual_fr_FR.pdf → manual_fr.pdf} +0 -0
  27. novelwriter/assets/sample.zip +0 -0
  28. novelwriter/assets/syntax/cyberpunk_night.conf +1 -1
  29. novelwriter/assets/text/credits_en.htm +6 -6
  30. novelwriter/assets/themes/cyberpunk_night.conf +23 -7
  31. novelwriter/assets/themes/default_dark.conf +20 -4
  32. novelwriter/assets/themes/default_light.conf +21 -5
  33. novelwriter/assets/themes/dracula.conf +20 -4
  34. novelwriter/assets/themes/solarized_dark.conf +24 -8
  35. novelwriter/assets/themes/solarized_light.conf +22 -6
  36. novelwriter/common.py +33 -26
  37. novelwriter/config.py +118 -127
  38. novelwriter/constants.py +75 -56
  39. novelwriter/core/buildsettings.py +23 -16
  40. novelwriter/core/coretools.py +11 -7
  41. novelwriter/core/docbuild.py +19 -13
  42. novelwriter/core/document.py +2 -2
  43. novelwriter/core/index.py +142 -432
  44. novelwriter/core/indexdata.py +403 -0
  45. novelwriter/core/item.py +35 -28
  46. novelwriter/core/itemmodel.py +27 -26
  47. novelwriter/core/novelmodel.py +223 -0
  48. novelwriter/core/options.py +1 -1
  49. novelwriter/core/project.py +10 -11
  50. novelwriter/core/projectdata.py +5 -5
  51. novelwriter/core/projectxml.py +1 -1
  52. novelwriter/core/sessions.py +3 -2
  53. novelwriter/core/spellcheck.py +4 -3
  54. novelwriter/core/status.py +12 -15
  55. novelwriter/core/storage.py +1 -1
  56. novelwriter/core/tree.py +46 -8
  57. novelwriter/dialogs/about.py +19 -22
  58. novelwriter/dialogs/docmerge.py +21 -23
  59. novelwriter/dialogs/docsplit.py +20 -23
  60. novelwriter/dialogs/editlabel.py +9 -13
  61. novelwriter/dialogs/preferences.py +111 -48
  62. novelwriter/dialogs/projectsettings.py +48 -54
  63. novelwriter/dialogs/quotes.py +14 -19
  64. novelwriter/dialogs/wordlist.py +25 -30
  65. novelwriter/enum.py +8 -0
  66. novelwriter/error.py +16 -16
  67. novelwriter/extensions/configlayout.py +18 -18
  68. novelwriter/extensions/eventfilters.py +9 -5
  69. novelwriter/extensions/modified.py +34 -15
  70. novelwriter/extensions/novelselector.py +18 -5
  71. novelwriter/extensions/pagedsidebar.py +39 -49
  72. novelwriter/extensions/progressbars.py +10 -8
  73. novelwriter/extensions/statusled.py +6 -13
  74. novelwriter/extensions/switch.py +19 -30
  75. novelwriter/extensions/switchbox.py +7 -3
  76. novelwriter/extensions/versioninfo.py +4 -4
  77. novelwriter/formats/shared.py +1 -1
  78. novelwriter/formats/todocx.py +14 -10
  79. novelwriter/formats/tohtml.py +7 -5
  80. novelwriter/formats/tokenizer.py +36 -33
  81. novelwriter/formats/tomarkdown.py +6 -2
  82. novelwriter/formats/toodt.py +27 -22
  83. novelwriter/formats/toqdoc.py +19 -14
  84. novelwriter/formats/toraw.py +6 -2
  85. novelwriter/gui/doceditor.py +216 -265
  86. novelwriter/gui/dochighlight.py +46 -45
  87. novelwriter/gui/docviewer.py +102 -104
  88. novelwriter/gui/docviewerpanel.py +47 -51
  89. novelwriter/gui/editordocument.py +8 -5
  90. novelwriter/gui/itemdetails.py +15 -18
  91. novelwriter/gui/mainmenu.py +147 -146
  92. novelwriter/gui/noveltree.py +239 -405
  93. novelwriter/gui/outline.py +137 -76
  94. novelwriter/gui/projtree.py +138 -132
  95. novelwriter/gui/search.py +33 -31
  96. novelwriter/gui/sidebar.py +13 -18
  97. novelwriter/gui/statusbar.py +13 -15
  98. novelwriter/gui/theme.py +533 -430
  99. novelwriter/guimain.py +27 -29
  100. novelwriter/shared.py +32 -23
  101. novelwriter/text/comments.py +70 -0
  102. novelwriter/text/patterns.py +4 -4
  103. novelwriter/tools/dictionaries.py +20 -29
  104. novelwriter/tools/lipsum.py +16 -17
  105. novelwriter/tools/manusbuild.py +39 -42
  106. novelwriter/tools/manuscript.py +113 -126
  107. novelwriter/tools/manussettings.py +78 -83
  108. novelwriter/tools/noveldetails.py +51 -64
  109. novelwriter/tools/welcome.py +56 -75
  110. novelwriter/tools/writingstats.py +44 -57
  111. novelwriter/types.py +5 -7
  112. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/METADATA +6 -6
  113. novelwriter-2.7b1.dist-info/RECORD +159 -0
  114. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/WHEEL +1 -1
  115. novelWriter-2.6.2.dist-info/RECORD +0 -363
  116. novelwriter/assets/icons/typicons_dark/README.md +0 -29
  117. novelwriter/assets/icons/typicons_dark/icons.conf +0 -134
  118. novelwriter/assets/icons/typicons_dark/mixed_copy.svg +0 -4
  119. novelwriter/assets/icons/typicons_dark/mixed_document-chapter.svg +0 -12
  120. novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +0 -6
  121. novelwriter/assets/icons/typicons_dark/mixed_document-note.svg +0 -12
  122. novelwriter/assets/icons/typicons_dark/mixed_document-scene.svg +0 -12
  123. novelwriter/assets/icons/typicons_dark/mixed_document-section.svg +0 -12
  124. novelwriter/assets/icons/typicons_dark/mixed_document-title.svg +0 -12
  125. novelwriter/assets/icons/typicons_dark/mixed_edit.svg +0 -4
  126. novelwriter/assets/icons/typicons_dark/mixed_import.svg +0 -5
  127. novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg +0 -5
  128. novelwriter/assets/icons/typicons_dark/mixed_input-none.svg +0 -5
  129. novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg +0 -5
  130. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +0 -6
  131. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +0 -6
  132. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +0 -6
  133. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +0 -6
  134. novelwriter/assets/icons/typicons_dark/mixed_search-replace.svg +0 -6
  135. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +0 -6
  136. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +0 -6
  137. novelwriter/assets/icons/typicons_dark/nw_deco-h0.svg +0 -4
  138. novelwriter/assets/icons/typicons_dark/nw_deco-h1.svg +0 -4
  139. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +0 -4
  140. novelwriter/assets/icons/typicons_dark/nw_deco-h2.svg +0 -4
  141. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +0 -4
  142. novelwriter/assets/icons/typicons_dark/nw_deco-h3.svg +0 -4
  143. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +0 -4
  144. novelwriter/assets/icons/typicons_dark/nw_deco-h4.svg +0 -4
  145. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +0 -4
  146. novelwriter/assets/icons/typicons_dark/nw_deco-noveltree-more.svg +0 -4
  147. novelwriter/assets/icons/typicons_dark/nw_font.svg +0 -4
  148. novelwriter/assets/icons/typicons_dark/nw_panel.svg +0 -4
  149. novelwriter/assets/icons/typicons_dark/nw_quote.svg +0 -4
  150. novelwriter/assets/icons/typicons_dark/nw_search-case.svg +0 -4
  151. novelwriter/assets/icons/typicons_dark/nw_search-preserve.svg +0 -4
  152. novelwriter/assets/icons/typicons_dark/nw_search-regex.svg +0 -4
  153. novelwriter/assets/icons/typicons_dark/nw_search-word.svg +0 -4
  154. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +0 -4
  155. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +0 -6
  156. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +0 -4
  157. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +0 -6
  158. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +0 -7
  159. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +0 -4
  160. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +0 -6
  161. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +0 -7
  162. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +0 -7
  163. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +0 -7
  164. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +0 -5
  165. novelwriter/assets/icons/typicons_dark/typ_arrow-down-thick-grey.svg +0 -4
  166. novelwriter/assets/icons/typicons_dark/typ_arrow-forward.svg +0 -4
  167. novelwriter/assets/icons/typicons_dark/typ_arrow-maximise.svg +0 -4
  168. novelwriter/assets/icons/typicons_dark/typ_arrow-minimise.svg +0 -4
  169. novelwriter/assets/icons/typicons_dark/typ_arrow-repeat-grey.svg +0 -4
  170. novelwriter/assets/icons/typicons_dark/typ_book-grey.svg +0 -4
  171. novelwriter/assets/icons/typicons_dark/typ_book.svg +0 -6
  172. novelwriter/assets/icons/typicons_dark/typ_bookmark.svg +0 -4
  173. novelwriter/assets/icons/typicons_dark/typ_calendar.svg +0 -4
  174. novelwriter/assets/icons/typicons_dark/typ_cancel-grey.svg +0 -4
  175. novelwriter/assets/icons/typicons_dark/typ_cancel.svg +0 -4
  176. novelwriter/assets/icons/typicons_dark/typ_chart-bar-grey.svg +0 -4
  177. novelwriter/assets/icons/typicons_dark/typ_chevron-down.svg +0 -4
  178. novelwriter/assets/icons/typicons_dark/typ_chevron-left.svg +0 -4
  179. novelwriter/assets/icons/typicons_dark/typ_chevron-right.svg +0 -4
  180. novelwriter/assets/icons/typicons_dark/typ_chevron-up.svg +0 -4
  181. novelwriter/assets/icons/typicons_dark/typ_cog.svg +0 -4
  182. novelwriter/assets/icons/typicons_dark/typ_delete-full.svg +0 -4
  183. novelwriter/assets/icons/typicons_dark/typ_delete.svg +0 -4
  184. novelwriter/assets/icons/typicons_dark/typ_directions-full.svg +0 -4
  185. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +0 -4
  186. novelwriter/assets/icons/typicons_dark/typ_document-text.svg +0 -8
  187. novelwriter/assets/icons/typicons_dark/typ_document.svg +0 -4
  188. novelwriter/assets/icons/typicons_dark/typ_export-grey.svg +0 -4
  189. novelwriter/assets/icons/typicons_dark/typ_export.svg +0 -4
  190. novelwriter/assets/icons/typicons_dark/typ_eye.svg +0 -4
  191. novelwriter/assets/icons/typicons_dark/typ_flag.svg +0 -4
  192. novelwriter/assets/icons/typicons_dark/typ_folder-open.svg +0 -4
  193. novelwriter/assets/icons/typicons_dark/typ_folder.svg +0 -5
  194. novelwriter/assets/icons/typicons_dark/typ_globe-grey.svg +0 -4
  195. novelwriter/assets/icons/typicons_dark/typ_key.svg +0 -4
  196. novelwriter/assets/icons/typicons_dark/typ_lightbulb-full.svg +0 -4
  197. novelwriter/assets/icons/typicons_dark/typ_location.svg +0 -4
  198. novelwriter/assets/icons/typicons_dark/typ_media-pause-grey.svg +0 -4
  199. novelwriter/assets/icons/typicons_dark/typ_media-record-outline.svg +0 -4
  200. novelwriter/assets/icons/typicons_dark/typ_media-record.svg +0 -4
  201. novelwriter/assets/icons/typicons_dark/typ_minus.svg +0 -4
  202. novelwriter/assets/icons/typicons_dark/typ_pencil.svg +0 -5
  203. novelwriter/assets/icons/typicons_dark/typ_pin-outline.svg +0 -4
  204. novelwriter/assets/icons/typicons_dark/typ_pin.svg +0 -4
  205. novelwriter/assets/icons/typicons_dark/typ_plus.svg +0 -4
  206. novelwriter/assets/icons/typicons_dark/typ_puzzle-outline.svg +0 -4
  207. novelwriter/assets/icons/typicons_dark/typ_puzzle.svg +0 -4
  208. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +0 -4
  209. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +0 -4
  210. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +0 -4
  211. novelwriter/assets/icons/typicons_dark/typ_search.svg +0 -4
  212. novelwriter/assets/icons/typicons_dark/typ_star.svg +0 -4
  213. novelwriter/assets/icons/typicons_dark/typ_stopwatch-grey.svg +0 -4
  214. novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +0 -4
  215. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +0 -4
  216. novelwriter/assets/icons/typicons_dark/typ_th-list-grey.svg +0 -4
  217. novelwriter/assets/icons/typicons_dark/typ_th-list.svg +0 -9
  218. novelwriter/assets/icons/typicons_dark/typ_times.svg +0 -4
  219. novelwriter/assets/icons/typicons_dark/typ_trash.svg +0 -5
  220. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +0 -4
  221. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +0 -4
  222. novelwriter/assets/icons/typicons_dark/typ_user.svg +0 -5
  223. novelwriter/assets/icons/typicons_dark/typ_warning-full.svg +0 -4
  224. novelwriter/assets/icons/typicons_light/README.md +0 -29
  225. novelwriter/assets/icons/typicons_light/icons.conf +0 -134
  226. novelwriter/assets/icons/typicons_light/mixed_copy.svg +0 -4
  227. novelwriter/assets/icons/typicons_light/mixed_document-chapter.svg +0 -12
  228. novelwriter/assets/icons/typicons_light/mixed_document-new.svg +0 -6
  229. novelwriter/assets/icons/typicons_light/mixed_document-note.svg +0 -12
  230. novelwriter/assets/icons/typicons_light/mixed_document-scene.svg +0 -12
  231. novelwriter/assets/icons/typicons_light/mixed_document-section.svg +0 -12
  232. novelwriter/assets/icons/typicons_light/mixed_document-title.svg +0 -12
  233. novelwriter/assets/icons/typicons_light/mixed_edit.svg +0 -4
  234. novelwriter/assets/icons/typicons_light/mixed_import.svg +0 -5
  235. novelwriter/assets/icons/typicons_light/mixed_input-checked.svg +0 -5
  236. novelwriter/assets/icons/typicons_light/mixed_input-none.svg +0 -5
  237. novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg +0 -5
  238. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +0 -6
  239. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +0 -6
  240. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +0 -6
  241. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +0 -6
  242. novelwriter/assets/icons/typicons_light/mixed_search-replace.svg +0 -6
  243. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +0 -6
  244. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +0 -6
  245. novelwriter/assets/icons/typicons_light/nw_deco-h0.svg +0 -4
  246. novelwriter/assets/icons/typicons_light/nw_deco-h1.svg +0 -4
  247. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +0 -4
  248. novelwriter/assets/icons/typicons_light/nw_deco-h2.svg +0 -4
  249. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +0 -4
  250. novelwriter/assets/icons/typicons_light/nw_deco-h3.svg +0 -4
  251. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +0 -4
  252. novelwriter/assets/icons/typicons_light/nw_deco-h4.svg +0 -4
  253. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +0 -4
  254. novelwriter/assets/icons/typicons_light/nw_deco-noveltree-more.svg +0 -4
  255. novelwriter/assets/icons/typicons_light/nw_font.svg +0 -4
  256. novelwriter/assets/icons/typicons_light/nw_panel.svg +0 -4
  257. novelwriter/assets/icons/typicons_light/nw_quote.svg +0 -4
  258. novelwriter/assets/icons/typicons_light/nw_search-case.svg +0 -4
  259. novelwriter/assets/icons/typicons_light/nw_search-preserve.svg +0 -4
  260. novelwriter/assets/icons/typicons_light/nw_search-regex.svg +0 -4
  261. novelwriter/assets/icons/typicons_light/nw_search-word.svg +0 -4
  262. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +0 -4
  263. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +0 -6
  264. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +0 -4
  265. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +0 -6
  266. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +0 -7
  267. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +0 -4
  268. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +0 -6
  269. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +0 -7
  270. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +0 -7
  271. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +0 -7
  272. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +0 -5
  273. novelwriter/assets/icons/typicons_light/typ_arrow-down-thick-grey.svg +0 -4
  274. novelwriter/assets/icons/typicons_light/typ_arrow-forward.svg +0 -4
  275. novelwriter/assets/icons/typicons_light/typ_arrow-maximise.svg +0 -4
  276. novelwriter/assets/icons/typicons_light/typ_arrow-minimise.svg +0 -4
  277. novelwriter/assets/icons/typicons_light/typ_arrow-repeat-grey.svg +0 -4
  278. novelwriter/assets/icons/typicons_light/typ_book-grey.svg +0 -4
  279. novelwriter/assets/icons/typicons_light/typ_book.svg +0 -6
  280. novelwriter/assets/icons/typicons_light/typ_bookmark.svg +0 -4
  281. novelwriter/assets/icons/typicons_light/typ_calendar.svg +0 -4
  282. novelwriter/assets/icons/typicons_light/typ_cancel-grey.svg +0 -4
  283. novelwriter/assets/icons/typicons_light/typ_cancel.svg +0 -4
  284. novelwriter/assets/icons/typicons_light/typ_chart-bar-grey.svg +0 -4
  285. novelwriter/assets/icons/typicons_light/typ_chevron-down.svg +0 -4
  286. novelwriter/assets/icons/typicons_light/typ_chevron-left.svg +0 -4
  287. novelwriter/assets/icons/typicons_light/typ_chevron-right.svg +0 -4
  288. novelwriter/assets/icons/typicons_light/typ_chevron-up.svg +0 -4
  289. novelwriter/assets/icons/typicons_light/typ_cog.svg +0 -4
  290. novelwriter/assets/icons/typicons_light/typ_delete-full.svg +0 -4
  291. novelwriter/assets/icons/typicons_light/typ_delete.svg +0 -4
  292. novelwriter/assets/icons/typicons_light/typ_directions-full.svg +0 -4
  293. novelwriter/assets/icons/typicons_light/typ_document-add.svg +0 -4
  294. novelwriter/assets/icons/typicons_light/typ_document-text.svg +0 -5
  295. novelwriter/assets/icons/typicons_light/typ_document.svg +0 -4
  296. novelwriter/assets/icons/typicons_light/typ_export-grey.svg +0 -4
  297. novelwriter/assets/icons/typicons_light/typ_export.svg +0 -4
  298. novelwriter/assets/icons/typicons_light/typ_eye.svg +0 -4
  299. novelwriter/assets/icons/typicons_light/typ_flag.svg +0 -4
  300. novelwriter/assets/icons/typicons_light/typ_folder-open.svg +0 -4
  301. novelwriter/assets/icons/typicons_light/typ_folder.svg +0 -5
  302. novelwriter/assets/icons/typicons_light/typ_globe-grey.svg +0 -4
  303. novelwriter/assets/icons/typicons_light/typ_key.svg +0 -4
  304. novelwriter/assets/icons/typicons_light/typ_lightbulb-full.svg +0 -4
  305. novelwriter/assets/icons/typicons_light/typ_location.svg +0 -4
  306. novelwriter/assets/icons/typicons_light/typ_media-pause-grey.svg +0 -4
  307. novelwriter/assets/icons/typicons_light/typ_media-record-outline.svg +0 -4
  308. novelwriter/assets/icons/typicons_light/typ_media-record.svg +0 -4
  309. novelwriter/assets/icons/typicons_light/typ_minus.svg +0 -4
  310. novelwriter/assets/icons/typicons_light/typ_pencil.svg +0 -5
  311. novelwriter/assets/icons/typicons_light/typ_pin-outline.svg +0 -4
  312. novelwriter/assets/icons/typicons_light/typ_pin.svg +0 -4
  313. novelwriter/assets/icons/typicons_light/typ_plus.svg +0 -4
  314. novelwriter/assets/icons/typicons_light/typ_puzzle-outline.svg +0 -4
  315. novelwriter/assets/icons/typicons_light/typ_puzzle.svg +0 -4
  316. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +0 -4
  317. novelwriter/assets/icons/typicons_light/typ_refresh.svg +0 -4
  318. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +0 -4
  319. novelwriter/assets/icons/typicons_light/typ_search.svg +0 -4
  320. novelwriter/assets/icons/typicons_light/typ_star.svg +0 -4
  321. novelwriter/assets/icons/typicons_light/typ_stopwatch-grey.svg +0 -4
  322. novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +0 -4
  323. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +0 -4
  324. novelwriter/assets/icons/typicons_light/typ_th-list-grey.svg +0 -4
  325. novelwriter/assets/icons/typicons_light/typ_th-list.svg +0 -9
  326. novelwriter/assets/icons/typicons_light/typ_times.svg +0 -4
  327. novelwriter/assets/icons/typicons_light/typ_trash.svg +0 -5
  328. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +0 -4
  329. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +0 -4
  330. novelwriter/assets/icons/typicons_light/typ_user.svg +0 -5
  331. novelwriter/assets/icons/typicons_light/typ_warning-full.svg +0 -4
  332. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/entry_points.txt +0 -0
  333. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info/licenses}/LICENSE.md +0 -0
  334. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/top_level.txt +0 -0
@@ -37,29 +37,31 @@ import logging
37
37
  from enum import Enum, IntFlag
38
38
  from time import time
39
39
 
40
- from PyQt5.QtCore import (
40
+ from PyQt6.QtCore import (
41
41
  QObject, QPoint, QRegularExpression, QRunnable, Qt, QTimer, pyqtSignal,
42
42
  pyqtSlot
43
43
  )
44
- from PyQt5.QtGui import (
45
- QColor, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent, QKeyEvent,
46
- QKeySequence, QMouseEvent, QPalette, QPixmap, QResizeEvent, QTextBlock,
47
- QTextCursor, QTextDocument, QTextOption
44
+ from PyQt6.QtGui import (
45
+ QAction, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent, QKeyEvent,
46
+ QKeySequence, QMouseEvent, QPalette, QPixmap, QResizeEvent, QShortcut,
47
+ QTextBlock, QTextCursor, QTextDocument, QTextOption
48
48
  )
49
- from PyQt5.QtWidgets import (
50
- QAction, QApplication, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit,
51
- QMenu, QPlainTextEdit, QShortcut, QToolBar, QVBoxLayout, QWidget
49
+ from PyQt6.QtWidgets import (
50
+ QApplication, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
51
+ QPlainTextEdit, QToolBar, QVBoxLayout, QWidget
52
52
  )
53
53
 
54
54
  from novelwriter import CONFIG, SHARED
55
- from novelwriter.common import decodeMimeHandles, fontMatcher, minmax, qtLambda, transferCase
55
+ from novelwriter.common import (
56
+ decodeMimeHandles, fontMatcher, minmax, qtAddAction, qtLambda, transferCase
57
+ )
56
58
  from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
57
59
  from novelwriter.core.document import NWDocument
58
60
  from novelwriter.enum import (
59
61
  nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass,
60
62
  nwItemType
61
63
  )
62
- from novelwriter.extensions.configlayout import NColourLabel
64
+ from novelwriter.extensions.configlayout import NColorLabel
63
65
  from novelwriter.extensions.eventfilters import WheelEventFilter
64
66
  from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton
65
67
  from novelwriter.gui.dochighlight import BLOCK_META, BLOCK_TITLE
@@ -95,16 +97,17 @@ class GuiDocEditor(QPlainTextEdit):
95
97
  """Gui Widget: Main Document Editor"""
96
98
 
97
99
  __slots__ = (
98
- "_nwDocument", "_nwItem", "_docChanged", "_docHandle", "_vpMargin",
99
- "_lastEdit", "_lastActive", "_lastFind", "_doReplace", "_autoReplace",
100
- "_completer", "_qDocument", "_keyContext", "_followTag1", "_followTag2",
101
- "_timerDoc", "_wCounterDoc", "_timerSel", "_wCounterSel",
100
+ "_autoReplace", "_completer", "_doReplace", "_docChanged", "_docHandle", "_followTag1",
101
+ "_followTag2", "_keyContext", "_lastActive", "_lastEdit", "_lastFind", "_nwDocument",
102
+ "_nwItem", "_qDocument", "_timerDoc", "_timerSel", "_vpMargin", "_wCounterDoc",
103
+ "_wCounterSel",
102
104
  )
103
105
 
104
106
  MOVE_KEYS = (
105
107
  Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up, Qt.Key.Key_Down,
106
108
  Qt.Key.Key_PageUp, Qt.Key.Key_PageDown
107
109
  )
110
+ ENTER_KEYS = (Qt.Key.Key_Return, Qt.Key.Key_Enter)
108
111
 
109
112
  # Custom Signals
110
113
  closeEditorRequest = pyqtSignal()
@@ -112,8 +115,6 @@ class GuiDocEditor(QPlainTextEdit):
112
115
  editedStatusChanged = pyqtSignal(bool)
113
116
  itemHandleChanged = pyqtSignal(str)
114
117
  loadDocumentTagRequest = pyqtSignal(str, Enum)
115
- novelItemMetaChanged = pyqtSignal(str)
116
- novelStructureChanged = pyqtSignal()
117
118
  openDocumentRequest = pyqtSignal(str, Enum, str, bool)
118
119
  requestNewNoteCreation = pyqtSignal(str, nwItemClass)
119
120
  requestNextDocument = pyqtSignal(str, bool)
@@ -175,7 +176,7 @@ class GuiDocEditor(QPlainTextEdit):
175
176
  self.customContextMenuRequested.connect(self._openContextMenu)
176
177
 
177
178
  # Editor Settings
178
- self.setMinimumWidth(CONFIG.pxInt(300))
179
+ self.setMinimumWidth(300)
179
180
  self.setAutoFillBackground(True)
180
181
  self.setFrameStyle(QFrame.Shape.NoFrame)
181
182
  self.setAcceptDrops(True)
@@ -224,7 +225,7 @@ class GuiDocEditor(QPlainTextEdit):
224
225
  self.changeFocusState = self.docHeader.changeFocusState
225
226
 
226
227
  # Finalise
227
- self.updateSyntaxColours()
228
+ self.updateSyntaxColors()
228
229
  self.initEditor()
229
230
 
230
231
  logger.debug("Ready: GuiDocEditor")
@@ -292,21 +293,24 @@ class GuiDocEditor(QPlainTextEdit):
292
293
  self.docToolBar.updateTheme()
293
294
  return
294
295
 
295
- def updateSyntaxColours(self) -> None:
296
+ def updateSyntaxColors(self) -> None:
296
297
  """Update the syntax highlighting theme."""
297
- mainPalette = self.palette()
298
- mainPalette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
299
- mainPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
300
- mainPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
301
- self.setPalette(mainPalette)
298
+ syntax = SHARED.theme.syntaxTheme
299
+
300
+ palette = self.palette()
301
+ palette.setColor(QPalette.ColorRole.Window, syntax.back)
302
+ palette.setColor(QPalette.ColorRole.Base, syntax.back)
303
+ palette.setColor(QPalette.ColorRole.Text, syntax.text)
304
+ self.setPalette(palette)
302
305
 
303
- docPalette = self.viewport().palette()
304
- docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
305
- docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
306
- self.viewport().setPalette(docPalette)
306
+ if viewport := self.viewport():
307
+ palette = viewport.palette()
308
+ palette.setColor(QPalette.ColorRole.Base, syntax.back)
309
+ palette.setColor(QPalette.ColorRole.Text, syntax.text)
310
+ viewport.setPalette(palette)
307
311
 
308
- self.docHeader.matchColours()
309
- self.docFooter.matchColours()
312
+ self.docHeader.matchColors()
313
+ self.docFooter.matchColors()
310
314
 
311
315
  return
312
316
 
@@ -336,19 +340,17 @@ class GuiDocEditor(QPlainTextEdit):
336
340
  # Due to cursor visibility, a part of the margin must be
337
341
  # allocated to the document itself. See issue #1112.
338
342
  self._qDocument.setDocumentMargin(4)
339
- self._vpMargin = max(CONFIG.getTextMargin() - 4, 0)
343
+ self._vpMargin = max(CONFIG.textMargin - 4, 0)
340
344
  self.setViewportMargins(self._vpMargin, self._vpMargin, self._vpMargin, self._vpMargin)
341
345
 
342
346
  # Also set the document text options for the document text flow
343
347
  options = QTextOption()
344
-
345
348
  if CONFIG.doJustify:
346
349
  options.setAlignment(QtAlignJustify)
347
350
  if CONFIG.showTabsNSpaces:
348
351
  options.setFlags(options.flags() | QTextOption.Flag.ShowTabsAndSpaces)
349
352
  if CONFIG.showLineEndings:
350
353
  options.setFlags(options.flags() | QTextOption.Flag.ShowLineAndParagraphSeparators)
351
-
352
354
  self._qDocument.setDefaultTextOption(options)
353
355
 
354
356
  # Scrolling
@@ -363,8 +365,9 @@ class GuiDocEditor(QPlainTextEdit):
363
365
  else:
364
366
  self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
365
367
 
366
- # Refresh the tab stops
367
- self.setTabStopDistance(CONFIG.getTabWidth())
368
+ # Refresh sizes
369
+ self.setTabStopDistance(CONFIG.tabWidth)
370
+ self.setCursorWidth(CONFIG.cursorWidth)
368
371
 
369
372
  # If we have a document open, we should refresh it in case the
370
373
  # font changed, otherwise we just clear the editor entirely,
@@ -381,19 +384,19 @@ class GuiDocEditor(QPlainTextEdit):
381
384
  """Load text from a document into the editor. If we have an I/O
382
385
  error, we must handle this and clear the editor so that we don't
383
386
  risk overwriting the file if it exists. This can for instance
384
- happen of the file contains binary elements or an encoding that
387
+ happen if the file contains binary elements or an encoding that
385
388
  novelWriter does not support. If loading is successful, or the
386
389
  document is new (empty string), we set up the editor for editing
387
390
  the file.
388
391
  """
389
392
  self._nwDocument = SHARED.project.storage.getDocument(tHandle)
390
393
  self._nwItem = self._nwDocument.nwItem
391
- if not ((nwItem := self._nwItem) and nwItem.itemType == nwItemType.FILE):
394
+ if not (self._nwItem and self._nwItem.itemType == nwItemType.FILE):
392
395
  logger.debug("Requested item '%s' is not a document", tHandle)
393
396
  self.clearEditor()
394
397
  return False
395
398
 
396
- if (docText := self._nwDocument.readDocument()) is None:
399
+ if (text := self._nwDocument.readDocument()) is None:
397
400
  # There was an I/O error
398
401
  self.clearEditor()
399
402
  return False
@@ -402,7 +405,7 @@ class GuiDocEditor(QPlainTextEdit):
402
405
  self._docHandle = tHandle
403
406
 
404
407
  self._allowAutoReplace(False)
405
- self._qDocument.setTextContent(docText, tHandle)
408
+ self._qDocument.setTextContent(text, tHandle)
406
409
  self._allowAutoReplace(True)
407
410
  QApplication.processEvents()
408
411
 
@@ -417,7 +420,7 @@ class GuiDocEditor(QPlainTextEdit):
417
420
  if isinstance(tLine, int):
418
421
  self.setCursorLine(tLine)
419
422
  else:
420
- self.setCursorPosition(nwItem.cursorPos)
423
+ self.setCursorPosition(self._nwItem.cursorPos)
421
424
 
422
425
  self.docHeader.setHandle(tHandle)
423
426
  self.docFooter.setHandle(tHandle)
@@ -440,7 +443,7 @@ class GuiDocEditor(QPlainTextEdit):
440
443
  # Finalise
441
444
  QApplication.restoreOverrideCursor()
442
445
  self.updateStatusMessage.emit(
443
- self.tr("Opened Document: {0}").format(nwItem.itemName)
446
+ self.tr("Opened Document: {0}").format(self._nwItem.itemName)
444
447
  )
445
448
 
446
449
  return True
@@ -471,19 +474,17 @@ class GuiDocEditor(QPlainTextEdit):
471
474
  )
472
475
  return False
473
476
 
474
- docText = self.getText()
475
- cC, wC, pC = standardCounter(docText)
477
+ text = self.getText()
478
+ cC, wC, pC = standardCounter(text)
476
479
  self._updateDocCounts(cC, wC, pC)
477
480
 
478
- if not self._nwDocument.writeDocument(docText):
481
+ if not self._nwDocument.writeDocument(text):
479
482
  saveOk = False
480
- if self._nwDocument.hashError:
481
- msgYes = SHARED.question(self.tr(
482
- "This document has been changed outside of novelWriter "
483
- "while it was open. Overwrite the file on disk?"
484
- ))
485
- if msgYes:
486
- saveOk = self._nwDocument.writeDocument(docText, forceWrite=True)
483
+ if self._nwDocument.hashError and SHARED.question(self.tr(
484
+ "This document has been changed outside of novelWriter "
485
+ "while it was open. Overwrite the file on disk?"
486
+ )):
487
+ saveOk = self._nwDocument.writeDocument(text, forceWrite=True)
487
488
 
488
489
  if not saveOk:
489
490
  SHARED.error(
@@ -495,52 +496,35 @@ class GuiDocEditor(QPlainTextEdit):
495
496
 
496
497
  self.setDocumentChanged(False)
497
498
  self.docTextChanged.emit(self._docHandle, self._lastEdit)
499
+ SHARED.project.index.scanText(tHandle, text)
498
500
 
499
- oldHeader = self._nwItem.mainHeading
500
- oldCount = SHARED.project.index.getHandleHeaderCount(tHandle)
501
- SHARED.project.index.scanText(tHandle, docText)
502
- newHeader = self._nwItem.mainHeading
503
- newCount = SHARED.project.index.getHandleHeaderCount(tHandle)
504
-
505
- if self._nwItem.itemClass == nwItemClass.NOVEL:
506
- if oldCount == newCount:
507
- self.novelItemMetaChanged.emit(tHandle)
508
- else:
509
- self.novelStructureChanged.emit()
510
-
511
- if oldHeader != newHeader:
512
- self.docFooter.updateInfo()
513
-
514
- # Update the status bar
515
501
  self.updateStatusMessage.emit(self.tr("Saved Document: {0}").format(self._nwItem.itemName))
516
502
 
517
503
  return True
518
504
 
519
505
  def cursorIsVisible(self) -> bool:
520
506
  """Check if the cursor is visible in the editor."""
521
- return (
522
- 0 < self.cursorRect().top()
523
- and self.cursorRect().bottom() < self.viewport().height()
524
- )
507
+ viewport = self.viewport()
508
+ height = viewport.height() if viewport else 0
509
+ return 0 < self.cursorRect().top() and self.cursorRect().bottom() < height
525
510
 
526
511
  def ensureCursorVisibleNoCentre(self) -> None:
527
512
  """Ensure cursor is visible, but don't force it to centre."""
528
- cT = self.cursorRect().top()
529
- cB = self.cursorRect().bottom()
530
- vH = self.viewport().height()
531
- if cT < 0:
532
- count = 0
533
- vBar = self.verticalScrollBar()
534
- while self.cursorRect().top() < 0 and count < 100000:
535
- vBar.setValue(vBar.value() - 1)
536
- count += 1
537
- elif cB > vH:
538
- count = 0
539
- vBar = self.verticalScrollBar()
540
- while self.cursorRect().bottom() > vH and count < 100000:
541
- vBar.setValue(vBar.value() + 1)
542
- count += 1
543
- QApplication.processEvents()
513
+ if (viewport := self.viewport()) and (vBar := self.verticalScrollBar()):
514
+ cT = self.cursorRect().top()
515
+ cB = self.cursorRect().bottom()
516
+ vH = viewport.height()
517
+ if cT < 0:
518
+ count = 0
519
+ while self.cursorRect().top() < 0 and count < 100000:
520
+ vBar.setValue(vBar.value() - 1)
521
+ count += 1
522
+ elif cB > vH:
523
+ count = 0
524
+ while self.cursorRect().bottom() > vH and count < 100000:
525
+ vBar.setValue(vBar.value() + 1)
526
+ count += 1
527
+ QApplication.processEvents()
544
528
  return
545
529
 
546
530
  def updateDocMargins(self) -> None:
@@ -552,10 +536,10 @@ class GuiDocEditor(QPlainTextEdit):
552
536
  wH = self.height()
553
537
 
554
538
  vBar = self.verticalScrollBar()
555
- sW = vBar.width() if vBar.isVisible() else 0
539
+ sW = vBar.width() if vBar and vBar.isVisible() else 0
556
540
 
557
541
  hBar = self.horizontalScrollBar()
558
- sH = hBar.height() if hBar.isVisible() else 0
542
+ sH = hBar.height() if hBar and hBar.isVisible() else 0
559
543
 
560
544
  tM = self._vpMargin
561
545
  if CONFIG.textWidth > 0 or SHARED.focusMode:
@@ -593,7 +577,7 @@ class GuiDocEditor(QPlainTextEdit):
593
577
  QTextDocument->toRawText instead of toPlainText. The former preserves
594
578
  non-breaking spaces, the latter does not. We still want to get rid of
595
579
  paragraph and line separators though.
596
- See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
580
+ See: https://doc.qt.io/qt-6/qtextdocument.html#toPlainText
597
581
  """
598
582
  text = self._qDocument.toRawText()
599
583
  text = text.replace(nwUnicode.U_LSEP, "\n") # Line separators
@@ -631,10 +615,9 @@ class GuiDocEditor(QPlainTextEdit):
631
615
 
632
616
  def setCursorPosition(self, position: int) -> None:
633
617
  """Move the cursor to a given position in the document."""
634
- nChars = self._qDocument.characterCount()
635
- if nChars > 1 and isinstance(position, int):
618
+ if (chars := self._qDocument.characterCount()) > 1 and isinstance(position, int):
636
619
  cursor = self.textCursor()
637
- cursor.setPosition(minmax(position, 0, nChars-1))
620
+ cursor.setPosition(minmax(position, 0, chars-1))
638
621
  self.setTextCursor(cursor)
639
622
  self.centerCursor()
640
623
  return
@@ -697,12 +680,10 @@ class GuiDocEditor(QPlainTextEdit):
697
680
 
698
681
  def spellCheckDocument(self) -> None:
699
682
  """Rerun the highlighter to update spell checking status of the
700
- currently loaded text. The fastest way to do this, at least as
701
- of Qt 5.13, is to clear the text and put it back. This clears
702
- the undo stack, so we only do it for big documents.
683
+ currently loaded text.
703
684
  """
704
- logger.debug("Running spell checker")
705
685
  start = time()
686
+ logger.debug("Running spell checker")
706
687
  QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
707
688
  self._qDocument.syntaxHighlighter.rehighlight()
708
689
  QApplication.restoreOverrideCursor()
@@ -779,7 +760,7 @@ class GuiDocEditor(QPlainTextEdit):
779
760
  elif action == nwDocAction.REPL_SNG:
780
761
  self._replaceQuotes("'", CONFIG.fmtSQuoteOpen, CONFIG.fmtSQuoteClose)
781
762
  elif action == nwDocAction.REPL_DBL:
782
- self._replaceQuotes("\"", CONFIG.fmtDQuoteOpen, CONFIG.fmtDQuoteClose)
763
+ self._replaceQuotes('"', CONFIG.fmtDQuoteOpen, CONFIG.fmtDQuoteClose)
783
764
  elif action == nwDocAction.RM_BREAKS:
784
765
  self._removeInParLineBreaks()
785
766
  elif action == nwDocAction.ALIGN_L:
@@ -943,9 +924,7 @@ class GuiDocEditor(QPlainTextEdit):
943
924
  * We also handle automatic scrolling here.
944
925
  """
945
926
  self._lastActive = time()
946
- isReturn = event.key() == Qt.Key.Key_Return
947
- isReturn |= event.key() == Qt.Key.Key_Enter
948
- if isReturn and self.docSearch.anyFocus():
927
+ if self.docSearch.anyFocus() and event.key() in self.ENTER_KEYS:
949
928
  return
950
929
  elif event == QKeySequence.StandardKey.Redo:
951
930
  self.docAction(nwDocAction.REDO)
@@ -964,10 +943,9 @@ class GuiDocEditor(QPlainTextEdit):
964
943
  kMod = event.modifiers()
965
944
  okMod = kMod in (QtModNone, QtModShift)
966
945
  okKey = event.key() not in self.MOVE_KEYS
967
- if nPos != cPos and okMod and okKey:
968
- mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
969
- if cPos > mPos:
970
- vBar = self.verticalScrollBar()
946
+ if nPos != cPos and okMod and okKey and (viewport := self.viewport()):
947
+ mPos = CONFIG.autoScrollPos*0.01 * viewport.height()
948
+ if cPos > mPos and (vBar := self.verticalScrollBar()):
971
949
  vBar.setValue(vBar.value() + (1 if nPos > cPos else -1))
972
950
  else:
973
951
  super().keyPressEvent(event)
@@ -976,7 +954,7 @@ class GuiDocEditor(QPlainTextEdit):
976
954
 
977
955
  def dragEnterEvent(self, event: QDragEnterEvent) -> None:
978
956
  """Overload drag enter event to handle dragged items."""
979
- if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
957
+ if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
980
958
  event.acceptProposedAction()
981
959
  else:
982
960
  super().dragEnterEvent(event)
@@ -984,7 +962,7 @@ class GuiDocEditor(QPlainTextEdit):
984
962
 
985
963
  def dragMoveEvent(self, event: QDragMoveEvent) -> None:
986
964
  """Overload drag move event to handle dragged items."""
987
- if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
965
+ if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
988
966
  event.acceptProposedAction()
989
967
  else:
990
968
  super().dragMoveEvent(event)
@@ -992,15 +970,15 @@ class GuiDocEditor(QPlainTextEdit):
992
970
 
993
971
  def dropEvent(self, event: QDropEvent) -> None:
994
972
  """Overload drop event to handle dragged items."""
995
- if event.mimeData().hasFormat(nwConst.MIME_HANDLE):
996
- if handles := decodeMimeHandles(event.mimeData()):
973
+ if (data := event.mimeData()) and data.hasFormat(nwConst.MIME_HANDLE):
974
+ if handles := decodeMimeHandles(data):
997
975
  if SHARED.project.tree.checkType(handles[0], nwItemType.FILE):
998
976
  self.openDocumentRequest.emit(handles[0], nwDocMode.EDIT, "", True)
999
977
  else:
1000
978
  super().dropEvent(event)
1001
979
  return
1002
980
 
1003
- def focusNextPrevChild(self, next: bool) -> bool:
981
+ def focusNextPrevChild(self, _next: bool) -> bool:
1004
982
  """Capture the focus request from the tab key on the text
1005
983
  editor. If the editor has focus, we do not change focus and
1006
984
  allow the editor to insert a tab. If the search bar has focus,
@@ -1059,7 +1037,7 @@ class GuiDocEditor(QPlainTextEdit):
1059
1037
  logger.error("Invalid keyword '%s'", keyword)
1060
1038
  return False
1061
1039
  logger.debug("Inserting keyword '%s'", keyword)
1062
- state = self.insertNewBlock("%s: " % keyword)
1040
+ state = self.insertNewBlock(f"{keyword}: ")
1063
1041
  return state
1064
1042
 
1065
1043
  @pyqtSlot()
@@ -1099,14 +1077,14 @@ class GuiDocEditor(QPlainTextEdit):
1099
1077
  if (block := self._qDocument.findBlock(pos)).isValid():
1100
1078
  text = block.text()
1101
1079
  if text.startswith("@") and added + removed == 1:
1102
- # Only run on single keypresses, otherwise it will trigger
1080
+ # Only run on single character changes, or it will trigger
1103
1081
  # at unwanted times when other changes are made to the document
1104
1082
  cursor = self.textCursor()
1105
1083
  bPos = cursor.positionInBlock()
1106
- if bPos > 0:
1084
+ if bPos > 0 and (viewport := self.viewport()):
1107
1085
  show = self._completer.updateText(text, bPos)
1108
1086
  point = self.cursorRect().bottomRight()
1109
- self._completer.move(self.viewport().mapToGlobal(point))
1087
+ self._completer.move(viewport.mapToGlobal(point))
1110
1088
  self._completer.setVisible(show)
1111
1089
  else:
1112
1090
  self._completer.setVisible(False)
@@ -1128,11 +1106,10 @@ class GuiDocEditor(QPlainTextEdit):
1128
1106
  def _insertCompletion(self, pos: int, length: int, text: str) -> None:
1129
1107
  """Insert choice from the completer menu."""
1130
1108
  cursor = self.textCursor()
1131
- block = cursor.block()
1132
- if block.isValid():
1133
- pos += block.position()
1134
- cursor.setPosition(pos, QtMoveAnchor)
1135
- cursor.setPosition(pos + length, QtKeepAnchor)
1109
+ if (block := cursor.block()).isValid():
1110
+ check = pos + block.position()
1111
+ cursor.setPosition(check, QtMoveAnchor)
1112
+ cursor.setPosition(check + length, QtKeepAnchor)
1136
1113
  cursor.insertText(text)
1137
1114
  self._completer.hide()
1138
1115
  return
@@ -1153,46 +1130,46 @@ class GuiDocEditor(QPlainTextEdit):
1153
1130
  ctxMenu = QMenu(self)
1154
1131
  ctxMenu.setObjectName("ContextMenu")
1155
1132
  if pBlock.userState() == BLOCK_TITLE:
1156
- action = ctxMenu.addAction(self.tr("Set as Document Name"))
1133
+ action = qtAddAction(ctxMenu, self.tr("Set as Document Name"))
1157
1134
  action.triggered.connect(qtLambda(self._emitRenameItem, pBlock))
1158
1135
 
1159
1136
  # URL
1160
1137
  (mData, mType) = self._qDocument.metaDataAtPos(pCursor.position())
1161
1138
  if mData and mType == "url":
1162
- action = ctxMenu.addAction(self.tr("Open URL"))
1139
+ action = qtAddAction(ctxMenu, self.tr("Open URL"))
1163
1140
  action.triggered.connect(qtLambda(SHARED.openWebsite, mData))
1164
1141
  ctxMenu.addSeparator()
1165
1142
 
1166
1143
  # Follow
1167
1144
  status = self._processTag(cursor=pCursor, follow=False)
1168
1145
  if status & _TagAction.FOLLOW:
1169
- action = ctxMenu.addAction(self.tr("Follow Tag"))
1146
+ action = qtAddAction(ctxMenu, self.tr("Follow Tag"))
1170
1147
  action.triggered.connect(qtLambda(self._processTag, cursor=pCursor, follow=True))
1171
1148
  ctxMenu.addSeparator()
1172
1149
  elif status & _TagAction.CREATE:
1173
- action = ctxMenu.addAction(self.tr("Create Note for Tag"))
1150
+ action = qtAddAction(ctxMenu, self.tr("Create Note for Tag"))
1174
1151
  action.triggered.connect(qtLambda(self._processTag, cursor=pCursor, create=True))
1175
1152
  ctxMenu.addSeparator()
1176
1153
 
1177
1154
  # Cut, Copy and Paste
1178
1155
  if uCursor.hasSelection():
1179
- action = ctxMenu.addAction(self.tr("Cut"))
1156
+ action = qtAddAction(ctxMenu, self.tr("Cut"))
1180
1157
  action.triggered.connect(qtLambda(self.docAction, nwDocAction.CUT))
1181
- action = ctxMenu.addAction(self.tr("Copy"))
1158
+ action = qtAddAction(ctxMenu, self.tr("Copy"))
1182
1159
  action.triggered.connect(qtLambda(self.docAction, nwDocAction.COPY))
1183
1160
 
1184
- action = ctxMenu.addAction(self.tr("Paste"))
1161
+ action = qtAddAction(ctxMenu, self.tr("Paste"))
1185
1162
  action.triggered.connect(qtLambda(self.docAction, nwDocAction.PASTE))
1186
1163
  ctxMenu.addSeparator()
1187
1164
 
1188
1165
  # Selections
1189
- action = ctxMenu.addAction(self.tr("Select All"))
1166
+ action = qtAddAction(ctxMenu, self.tr("Select All"))
1190
1167
  action.triggered.connect(qtLambda(self.docAction, nwDocAction.SEL_ALL))
1191
- action = ctxMenu.addAction(self.tr("Select Word"))
1168
+ action = qtAddAction(ctxMenu, self.tr("Select Word"))
1192
1169
  action.triggered.connect(qtLambda(
1193
1170
  self._makePosSelection, QTextCursor.SelectionType.WordUnderCursor, pos,
1194
1171
  ))
1195
- action = ctxMenu.addAction(self.tr("Select Paragraph"))
1172
+ action = qtAddAction(ctxMenu, self.tr("Select Paragraph"))
1196
1173
  action.triggered.connect(qtLambda(
1197
1174
  self._makePosSelection, QTextCursor.SelectionType.BlockUnderCursor, pos
1198
1175
  ))
@@ -1208,22 +1185,24 @@ class GuiDocEditor(QPlainTextEdit):
1208
1185
  sCursor.movePosition(QtMoveRight, QtKeepAnchor, cLen)
1209
1186
  if suggest:
1210
1187
  ctxMenu.addSeparator()
1211
- ctxMenu.addAction(self.tr("Spelling Suggestion(s)"))
1188
+ qtAddAction(ctxMenu, self.tr("Spelling Suggestion(s)"))
1212
1189
  for option in suggest[:15]:
1213
- action = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
1190
+ action = qtAddAction(ctxMenu, f"{nwUnicode.U_ENDASH} {option}")
1214
1191
  action.triggered.connect(qtLambda(self._correctWord, sCursor, option))
1215
1192
  else:
1216
1193
  trNone = self.tr("No Suggestions")
1217
- ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {trNone}")
1194
+ qtAddAction(ctxMenu, f"{nwUnicode.U_ENDASH} {trNone}")
1218
1195
 
1219
1196
  ctxMenu.addSeparator()
1220
- action = ctxMenu.addAction(self.tr("Ignore Word"))
1197
+ action = qtAddAction(ctxMenu, self.tr("Ignore Word"))
1221
1198
  action.triggered.connect(qtLambda(self._addWord, word, block, False))
1222
- action = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
1199
+ action = qtAddAction(ctxMenu, self.tr("Add Word to Dictionary"))
1223
1200
  action.triggered.connect(qtLambda(self._addWord, word, block, True))
1224
1201
 
1225
1202
  # Execute the context menu
1226
- ctxMenu.exec(self.viewport().mapToGlobal(pos))
1203
+ if viewport := self.viewport():
1204
+ ctxMenu.exec(viewport.mapToGlobal(pos))
1205
+
1227
1206
  ctxMenu.setParent(None)
1228
1207
 
1229
1208
  return
@@ -1281,15 +1260,11 @@ class GuiDocEditor(QPlainTextEdit):
1281
1260
  @pyqtSlot()
1282
1261
  def _runSelCounter(self) -> None:
1283
1262
  """Update the selection word count."""
1284
- if self._docHandle is None:
1285
- return
1286
-
1287
- if self._wCounterSel.isRunning():
1288
- logger.debug("Selection word counter is busy")
1289
- return
1290
-
1291
- SHARED.runInThreadPool(self._wCounterSel)
1292
-
1263
+ if self._docHandle:
1264
+ if self._wCounterSel.isRunning():
1265
+ logger.debug("Selection word counter is busy")
1266
+ return
1267
+ SHARED.runInThreadPool(self._wCounterSel)
1293
1268
  return
1294
1269
 
1295
1270
  @pyqtSlot(int, int, int)
@@ -1347,6 +1322,7 @@ class GuiDocEditor(QPlainTextEdit):
1347
1322
  self.beginSearch()
1348
1323
  return
1349
1324
 
1325
+ prevFocus = QApplication.focusWidget() or self
1350
1326
  resS, resE = self.findAllOccurences()
1351
1327
  if len(resS) == 0 and self._docHandle:
1352
1328
  self.docSearch.setResultCount(0, 0)
@@ -1355,7 +1331,7 @@ class GuiDocEditor(QPlainTextEdit):
1355
1331
  self.requestNextDocument.emit(self._docHandle, CONFIG.searchLoop)
1356
1332
  QApplication.processEvents()
1357
1333
  self.beginSearch()
1358
- self.setFocus()
1334
+ prevFocus.setFocus()
1359
1335
  return
1360
1336
 
1361
1337
  cursor = self.textCursor()
@@ -1375,7 +1351,7 @@ class GuiDocEditor(QPlainTextEdit):
1375
1351
  self.requestNextDocument.emit(self._docHandle, CONFIG.searchLoop)
1376
1352
  QApplication.processEvents()
1377
1353
  self.beginSearch()
1378
- self.setFocus()
1354
+ prevFocus.setFocus()
1379
1355
  return
1380
1356
  else:
1381
1357
  resIdx = 0 if doLoop else maxIdx
@@ -1552,10 +1528,10 @@ class GuiDocEditor(QPlainTextEdit):
1552
1528
  if fLen == min(numA, numB):
1553
1529
  cursor.beginEditBlock()
1554
1530
  cursor.setPosition(posS)
1555
- for i in range(fLen):
1531
+ for _ in range(fLen):
1556
1532
  cursor.deletePreviousChar()
1557
1533
  cursor.setPosition(posE)
1558
- for i in range(fLen):
1534
+ for _ in range(fLen):
1559
1535
  cursor.deletePreviousChar()
1560
1536
  cursor.endEditBlock()
1561
1537
 
@@ -1569,8 +1545,10 @@ class GuiDocEditor(QPlainTextEdit):
1569
1545
 
1570
1546
  return
1571
1547
 
1572
- def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
1573
- select: _SelectAction = _SelectAction.NO_DECISION) -> None:
1548
+ def _wrapSelection(
1549
+ self, before: str, after: str | None = None, pos: int | None = None,
1550
+ select: _SelectAction = _SelectAction.NO_DECISION
1551
+ ) -> None:
1574
1552
  """Wrap the selected text in whatever is in tBefore and tAfter.
1575
1553
  If there is no selection, the autoSelect setting decides the
1576
1554
  action. AutoSelect will select the word under the cursor before
@@ -1965,7 +1943,9 @@ class GuiDocEditor(QPlainTextEdit):
1965
1943
  exist = False
1966
1944
  cPos = cursor.selectionStart() - block.position()
1967
1945
  tExist = SHARED.project.index.checkThese(tBits, self._docHandle)
1968
- for sTag, sPos, sExist in zip(reversed(tBits), reversed(tPos), reversed(tExist)):
1946
+ for sTag, sPos, sExist in zip(
1947
+ reversed(tBits), reversed(tPos), reversed(tExist), strict=False
1948
+ ):
1969
1949
  if cPos >= sPos:
1970
1950
  # The cursor is between the start of two tags
1971
1951
  if cPos <= sPos + len(sTag):
@@ -2021,9 +2001,9 @@ class GuiDocEditor(QPlainTextEdit):
2021
2001
  sPos = cPos
2022
2002
  for i in range(cPos - bPos):
2023
2003
  sPos = cPos - i - 1
2024
- cOne = self._qDocument.characterAt(sPos)
2025
- cTwo = self._qDocument.characterAt(sPos - 1)
2026
- if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
2004
+ cOne = str(self._qDocument.characterAt(sPos))
2005
+ cTwo = str(self._qDocument.characterAt(sPos - 1))
2006
+ if not (cOne.isalnum() or (cOne in apos and cTwo.isalnum())):
2027
2007
  sPos += 1
2028
2008
  break
2029
2009
 
@@ -2031,9 +2011,9 @@ class GuiDocEditor(QPlainTextEdit):
2031
2011
  ePos = cPos
2032
2012
  for i in range(bPos + bLen - cPos):
2033
2013
  ePos = cPos + i
2034
- cOne = self._qDocument.characterAt(ePos)
2035
- cTwo = self._qDocument.characterAt(ePos + 1)
2036
- if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
2014
+ cOne = str(self._qDocument.characterAt(ePos))
2015
+ cTwo = str(self._qDocument.characterAt(ePos + 1))
2016
+ if not (cOne.isalnum() or (cOne in apos and cTwo.isalnum())):
2037
2017
  break
2038
2018
 
2039
2019
  if ePos - sPos <= 0:
@@ -2048,8 +2028,9 @@ class GuiDocEditor(QPlainTextEdit):
2048
2028
 
2049
2029
  return cursor
2050
2030
 
2051
- def _makeSelection(self, mode: QTextCursor.SelectionType,
2052
- cursor: QTextCursor | None = None) -> None:
2031
+ def _makeSelection(
2032
+ self, mode: QTextCursor.SelectionType, cursor: QTextCursor | None = None
2033
+ ) -> None:
2053
2034
  """Select text based on selection mode."""
2054
2035
  if cursor is None:
2055
2036
  cursor = self.textCursor()
@@ -2135,7 +2116,7 @@ class MetaCompleter(QMenu):
2135
2116
 
2136
2117
  for value in sorted(options):
2137
2118
  rep = value + suffix
2138
- action = self.addAction(value)
2119
+ action = qtAddAction(self, value)
2139
2120
  action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep))
2140
2121
 
2141
2122
  return True
@@ -2175,13 +2156,10 @@ class BackgroundWordCounter(QRunnable):
2175
2156
 
2176
2157
  def __init__(self, docEditor: GuiDocEditor, forSelection: bool = False) -> None:
2177
2158
  super().__init__()
2178
-
2179
2159
  self._docEditor = docEditor
2180
2160
  self._forSelection = forSelection
2181
2161
  self._isRunning = False
2182
-
2183
2162
  self.signals = BackgroundWordCounterSignals()
2184
-
2185
2163
  return
2186
2164
 
2187
2165
  def isRunning(self) -> bool:
@@ -2215,9 +2193,9 @@ class BackgroundWordCounterSignals(QObject):
2215
2193
  class TextAutoReplace:
2216
2194
 
2217
2195
  __slots__ = (
2218
- "_quoteSO", "_quoteSC", "_quoteDO", "_quoteDC",
2219
- "_replaceSQuote", "_replaceDQuote", "_replaceDash", "_replaceDots",
2220
- "_padChar", "_padBefore", "_padAfter", "_doPadBefore", "_doPadAfter",
2196
+ "_doPadAfter", "_doPadBefore", "_padAfter", "_padBefore", "_padChar",
2197
+ "_quoteDC", "_quoteDO", "_quoteSC", "_quoteSO", "_replaceDQuote",
2198
+ "_replaceDash", "_replaceDots", "_replaceSQuote",
2221
2199
  )
2222
2200
 
2223
2201
  def __init__(self) -> None:
@@ -2352,7 +2330,6 @@ class GuiDocToolBar(QWidget):
2352
2330
  logger.debug("Create: GuiDocToolBar")
2353
2331
 
2354
2332
  iSz = SHARED.theme.baseIconSize
2355
- cM = CONFIG.pxInt(4)
2356
2333
  self.setContentsMargins(0, 0, 0, 0)
2357
2334
 
2358
2335
  # General Buttons
@@ -2425,7 +2402,7 @@ class GuiDocToolBar(QWidget):
2425
2402
  self.outerBox.addWidget(self.tbBoldMD)
2426
2403
  self.outerBox.addWidget(self.tbItalicMD)
2427
2404
  self.outerBox.addWidget(self.tbStrikeMD)
2428
- self.outerBox.addSpacing(cM)
2405
+ self.outerBox.addSpacing(4)
2429
2406
  self.outerBox.addWidget(self.tbBold)
2430
2407
  self.outerBox.addWidget(self.tbItalic)
2431
2408
  self.outerBox.addWidget(self.tbStrike)
@@ -2433,8 +2410,8 @@ class GuiDocToolBar(QWidget):
2433
2410
  self.outerBox.addWidget(self.tbMark)
2434
2411
  self.outerBox.addWidget(self.tbSuperscript)
2435
2412
  self.outerBox.addWidget(self.tbSubscript)
2436
- self.outerBox.setContentsMargins(cM, cM, cM, cM)
2437
- self.outerBox.setSpacing(cM)
2413
+ self.outerBox.setContentsMargins(4, 4, 4, 4)
2414
+ self.outerBox.setSpacing(4)
2438
2415
 
2439
2416
  self.setLayout(self.outerBox)
2440
2417
  self.updateTheme()
@@ -2448,15 +2425,17 @@ class GuiDocToolBar(QWidget):
2448
2425
 
2449
2426
  def updateTheme(self) -> None:
2450
2427
  """Initialise GUI elements that depend on specific settings."""
2451
- palette = QPalette()
2452
- palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
2453
- palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
2454
- palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
2428
+ syntax = SHARED.theme.syntaxTheme
2429
+
2430
+ palette = self.palette()
2431
+ palette.setColor(QPalette.ColorRole.Window, syntax.back)
2432
+ palette.setColor(QPalette.ColorRole.WindowText, syntax.text)
2433
+ palette.setColor(QPalette.ColorRole.Text, syntax.text)
2455
2434
  self.setPalette(palette)
2456
2435
 
2457
- self.tbBoldMD.setThemeIcon("fmt_bold-md")
2458
- self.tbItalicMD.setThemeIcon("fmt_italic-md")
2459
- self.tbStrikeMD.setThemeIcon("fmt_strike-md")
2436
+ self.tbBoldMD.setThemeIcon("fmt_bold", "orange")
2437
+ self.tbItalicMD.setThemeIcon("fmt_italic", "orange")
2438
+ self.tbStrikeMD.setThemeIcon("fmt_strike", "orange")
2460
2439
  self.tbBold.setThemeIcon("fmt_bold")
2461
2440
  self.tbItalic.setThemeIcon("fmt_italic")
2462
2441
  self.tbStrike.setThemeIcon("fmt_strike")
@@ -2483,7 +2462,6 @@ class GuiDocEditSearch(QFrame):
2483
2462
  self.docEditor = docEditor
2484
2463
 
2485
2464
  iSz = SHARED.theme.baseIconSize
2486
- mPx = CONFIG.pxInt(6)
2487
2465
 
2488
2466
  self.setContentsMargins(0, 0, 0, 0)
2489
2467
  self.setAutoFillBackground(True)
@@ -2509,7 +2487,7 @@ class GuiDocEditSearch(QFrame):
2509
2487
  self.searchOpt.setContentsMargins(0, 0, 0, 0)
2510
2488
 
2511
2489
  self.searchLabel = QLabel(self.tr("Search"), self)
2512
- self.searchLabel.setIndent(CONFIG.pxInt(6))
2490
+ self.searchLabel.setIndent(6)
2513
2491
 
2514
2492
  self.resultLabel = QLabel("?/?", self)
2515
2493
 
@@ -2586,12 +2564,11 @@ class GuiDocEditSearch(QFrame):
2586
2564
  self.mainBox.setColumnStretch(3, 0)
2587
2565
  self.mainBox.setColumnStretch(4, 0)
2588
2566
  self.mainBox.setColumnStretch(5, 0)
2589
- self.mainBox.setSpacing(CONFIG.pxInt(2))
2590
- self.mainBox.setContentsMargins(mPx, mPx, mPx, mPx)
2567
+ self.mainBox.setSpacing(2)
2568
+ self.mainBox.setContentsMargins(6, 6, 6, 6)
2591
2569
 
2592
- boxWidth = CONFIG.pxInt(200)
2593
- self.searchBox.setFixedWidth(boxWidth)
2594
- self.replaceBox.setFixedWidth(boxWidth)
2570
+ self.searchBox.setFixedWidth(200)
2571
+ self.replaceBox.setFixedWidth(200)
2595
2572
  self.replaceBox.setVisible(False)
2596
2573
  self.replaceButton.setVisible(False)
2597
2574
  self.adjustSize()
@@ -2693,10 +2670,11 @@ class GuiDocEditSearch(QFrame):
2693
2670
 
2694
2671
  def updateTheme(self) -> None:
2695
2672
  """Update theme elements."""
2696
- qPalette = QApplication.palette()
2697
- self.setPalette(qPalette)
2698
- self.searchBox.setPalette(qPalette)
2699
- self.replaceBox.setPalette(qPalette)
2673
+ palette = QApplication.palette()
2674
+
2675
+ self.setPalette(palette)
2676
+ self.searchBox.setPalette(palette)
2677
+ self.replaceBox.setPalette(palette)
2700
2678
 
2701
2679
  # Set icons
2702
2680
  self.toggleCase.setIcon(SHARED.theme.getIcon("search_case"))
@@ -2706,31 +2684,13 @@ class GuiDocEditSearch(QFrame):
2706
2684
  self.toggleProject.setIcon(SHARED.theme.getIcon("search_project"))
2707
2685
  self.toggleMatchCap.setIcon(SHARED.theme.getIcon("search_preserve"))
2708
2686
  self.cancelSearch.setIcon(SHARED.theme.getIcon("search_cancel"))
2709
- self.searchButton.setThemeIcon("search")
2710
- self.replaceButton.setThemeIcon("search_replace")
2687
+ self.searchButton.setThemeIcon("search", "green")
2688
+ self.replaceButton.setThemeIcon("search_replace", "green")
2711
2689
 
2712
2690
  # Set stylesheets
2713
2691
  self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
2714
2692
  self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
2715
2693
 
2716
- # Construct Box Colours
2717
- qPalette = self.searchBox.palette()
2718
- baseCol = qPalette.base().color()
2719
- rCol = baseCol.redF() + 0.1
2720
- gCol = baseCol.greenF() - 0.1
2721
- bCol = baseCol.blueF() - 0.1
2722
-
2723
- mCol = max(rCol, gCol, bCol, 1.0)
2724
- errCol = QColor()
2725
- errCol.setRedF(rCol/mCol)
2726
- errCol.setGreenF(gCol/mCol)
2727
- errCol.setBlueF(bCol/mCol)
2728
-
2729
- self.rxCol = {
2730
- True: baseCol,
2731
- False: errCol
2732
- }
2733
-
2734
2694
  return
2735
2695
 
2736
2696
  def cycleFocus(self) -> bool:
@@ -2747,7 +2707,7 @@ class GuiDocEditSearch(QFrame):
2747
2707
 
2748
2708
  def anyFocus(self) -> bool:
2749
2709
  """Return True if any of the input boxes have focus."""
2750
- return self.searchBox.hasFocus() or self.replaceBox.hasFocus()
2710
+ return self.hasFocus() or self.isAncestorOf(QApplication.focusWidget())
2751
2711
 
2752
2712
  ##
2753
2713
  # Public Slots
@@ -2831,9 +2791,12 @@ class GuiDocEditSearch(QFrame):
2831
2791
  """Highlight the search box to indicate the search string is or
2832
2792
  isn't valid. Take the colour from the replace box.
2833
2793
  """
2834
- qPalette = self.replaceBox.palette()
2835
- qPalette.setColor(QPalette.ColorRole.Base, self.rxCol[isValid])
2836
- self.searchBox.setPalette(qPalette)
2794
+ palette = self.replaceBox.palette()
2795
+ palette.setColor(
2796
+ QPalette.ColorRole.Text,
2797
+ palette.text().color() if isValid else SHARED.theme.errorText
2798
+ )
2799
+ self.searchBox.setPalette(palette)
2837
2800
  return
2838
2801
 
2839
2802
 
@@ -2859,13 +2822,12 @@ class GuiDocEditHeader(QWidget):
2859
2822
 
2860
2823
  iPx = SHARED.theme.baseIconHeight
2861
2824
  iSz = SHARED.theme.baseIconSize
2862
- mPx = CONFIG.pxInt(4)
2863
2825
 
2864
2826
  # Main Widget Settings
2865
2827
  self.setAutoFillBackground(True)
2866
2828
 
2867
2829
  # Title Label
2868
- self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
2830
+ self.itemTitle = NColorLabel("", self, faded=SHARED.theme.fadedText)
2869
2831
  self.itemTitle.setMargin(0)
2870
2832
  self.itemTitle.setContentsMargins(0, 0, 0, 0)
2871
2833
  self.itemTitle.setAutoFillBackground(True)
@@ -2906,13 +2868,13 @@ class GuiDocEditHeader(QWidget):
2906
2868
  self.outerBox.addWidget(self.tbButton, 0)
2907
2869
  self.outerBox.addWidget(self.outlineButton, 0)
2908
2870
  self.outerBox.addWidget(self.searchButton, 0)
2909
- self.outerBox.addSpacing(mPx)
2871
+ self.outerBox.addSpacing(4)
2910
2872
  self.outerBox.addWidget(self.itemTitle, 1)
2911
- self.outerBox.addSpacing(mPx)
2873
+ self.outerBox.addSpacing(4)
2912
2874
  self.outerBox.addSpacing(iPx)
2913
2875
  self.outerBox.addWidget(self.minmaxButton, 0)
2914
2876
  self.outerBox.addWidget(self.closeButton, 0)
2915
- self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
2877
+ self.outerBox.setContentsMargins(4, 4, 4, 4)
2916
2878
  self.outerBox.setSpacing(0)
2917
2879
 
2918
2880
  self.setLayout(self.outerBox)
@@ -2923,7 +2885,7 @@ class GuiDocEditHeader(QWidget):
2923
2885
  # Fix Margins and Size
2924
2886
  # This is needed for high DPI systems. See issue #499.
2925
2887
  self.setContentsMargins(0, 0, 0, 0)
2926
- self.setMinimumHeight(iPx + 2*mPx)
2888
+ self.setMinimumHeight(iPx + 8)
2927
2889
 
2928
2890
  self.updateFont()
2929
2891
  self.updateTheme()
@@ -2956,7 +2918,7 @@ class GuiDocEditHeader(QWidget):
2956
2918
  tStart = time()
2957
2919
  self.outlineMenu.clear()
2958
2920
  for number, text in data.items():
2959
- action = self.outlineMenu.addAction(text)
2921
+ action = qtAddAction(self.outlineMenu, text)
2960
2922
  action.triggered.connect(qtLambda(self._gotoBlock, number))
2961
2923
  self._docOutline = data
2962
2924
  logger.debug("Document outline updated in %.3f ms", 1000*(time() - tStart))
@@ -2970,11 +2932,11 @@ class GuiDocEditHeader(QWidget):
2970
2932
 
2971
2933
  def updateTheme(self) -> None:
2972
2934
  """Update theme elements."""
2973
- self.tbButton.setThemeIcon("toolbar")
2974
- self.outlineButton.setThemeIcon("list")
2975
- self.searchButton.setThemeIcon("search")
2976
- self.minmaxButton.setThemeIcon("maximise")
2977
- self.closeButton.setThemeIcon("close")
2935
+ self.tbButton.setThemeIcon("fmt_toolbar", "blue")
2936
+ self.outlineButton.setThemeIcon("list", "blue")
2937
+ self.searchButton.setThemeIcon("search", "blue")
2938
+ self.minmaxButton.setThemeIcon("maximise", "blue")
2939
+ self.closeButton.setThemeIcon("close", "red")
2978
2940
 
2979
2941
  buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
2980
2942
  self.tbButton.setStyleSheet(buttonStyle)
@@ -2983,18 +2945,19 @@ class GuiDocEditHeader(QWidget):
2983
2945
  self.minmaxButton.setStyleSheet(buttonStyle)
2984
2946
  self.closeButton.setStyleSheet(buttonStyle)
2985
2947
 
2986
- self.matchColours()
2948
+ self.matchColors()
2987
2949
 
2988
2950
  return
2989
2951
 
2990
- def matchColours(self) -> None:
2952
+ def matchColors(self) -> None:
2991
2953
  """Update the colours of the widget to match those of the syntax
2992
2954
  theme rather than the main GUI.
2993
2955
  """
2994
- palette = QPalette()
2995
- palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
2996
- palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
2997
- palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
2956
+ syntax = SHARED.theme.syntaxTheme
2957
+ palette = self.palette()
2958
+ palette.setColor(QPalette.ColorRole.Window, syntax.back)
2959
+ palette.setColor(QPalette.ColorRole.WindowText, syntax.text)
2960
+ palette.setColor(QPalette.ColorRole.Text, syntax.text)
2998
2961
  self.setPalette(palette)
2999
2962
  self.itemTitle.setTextColors(
3000
2963
  color=palette.windowText().color(), faded=SHARED.theme.fadedText
@@ -3047,7 +3010,7 @@ class GuiDocEditHeader(QWidget):
3047
3010
  @pyqtSlot(bool)
3048
3011
  def _focusModeChanged(self, focusMode: bool) -> None:
3049
3012
  """Update minimise/maximise icon of the Focus Mode button."""
3050
- self.minmaxButton.setThemeIcon("minimise" if focusMode else "maximise")
3013
+ self.minmaxButton.setThemeIcon("minimise" if focusMode else "maximise", "blue")
3051
3014
  return
3052
3015
 
3053
3016
  ##
@@ -3080,9 +3043,6 @@ class GuiDocEditFooter(QWidget):
3080
3043
 
3081
3044
  iPx = round(0.9*SHARED.theme.baseIconHeight)
3082
3045
  fPx = int(0.9*SHARED.theme.fontPixelSize)
3083
- mPx = CONFIG.pxInt(8)
3084
- bSp = CONFIG.pxInt(4)
3085
- hSp = CONFIG.pxInt(6)
3086
3046
 
3087
3047
  # Cached Translations
3088
3048
  self._trLineCount = self.tr("Line: {0} ({1})")
@@ -3095,65 +3055,53 @@ class GuiDocEditFooter(QWidget):
3095
3055
 
3096
3056
  # Status
3097
3057
  self.statusIcon = QLabel("", self)
3098
- self.statusIcon.setContentsMargins(0, 0, 0, 0)
3099
3058
  self.statusIcon.setFixedHeight(iPx)
3100
3059
  self.statusIcon.setAlignment(QtAlignLeftTop)
3101
3060
 
3102
- self.statusText = QLabel(self.tr("Status"), self)
3103
- self.statusText.setIndent(0)
3104
- self.statusText.setMargin(0)
3105
- self.statusText.setContentsMargins(0, 0, 0, 0)
3061
+ self.statusText = QLabel("", self)
3106
3062
  self.statusText.setAutoFillBackground(True)
3107
3063
  self.statusText.setFixedHeight(fPx)
3108
3064
  self.statusText.setAlignment(QtAlignLeftTop)
3109
3065
 
3110
3066
  # Lines
3111
3067
  self.linesIcon = QLabel("", self)
3112
- self.linesIcon.setContentsMargins(0, 0, 0, 0)
3113
3068
  self.linesIcon.setFixedHeight(iPx)
3114
3069
  self.linesIcon.setAlignment(QtAlignLeftTop)
3115
3070
 
3116
3071
  self.linesText = QLabel("", self)
3117
- self.linesText.setIndent(0)
3118
- self.linesText.setMargin(0)
3119
- self.linesText.setContentsMargins(0, 0, 0, 0)
3120
3072
  self.linesText.setAutoFillBackground(True)
3121
3073
  self.linesText.setFixedHeight(fPx)
3122
3074
  self.linesText.setAlignment(QtAlignLeftTop)
3123
3075
 
3124
3076
  # Words
3125
3077
  self.wordsIcon = QLabel("", self)
3126
- self.wordsIcon.setContentsMargins(0, 0, 0, 0)
3127
3078
  self.wordsIcon.setFixedHeight(iPx)
3128
3079
  self.wordsIcon.setAlignment(QtAlignLeftTop)
3129
3080
 
3130
3081
  self.wordsText = QLabel("", self)
3131
- self.wordsText.setIndent(0)
3132
- self.wordsText.setMargin(0)
3133
- self.wordsText.setContentsMargins(0, 0, 0, 0)
3134
3082
  self.wordsText.setAutoFillBackground(True)
3135
3083
  self.wordsText.setFixedHeight(fPx)
3136
3084
  self.wordsText.setAlignment(QtAlignLeftTop)
3137
3085
 
3138
3086
  # Assemble Layout
3139
3087
  self.outerBox = QHBoxLayout()
3140
- self.outerBox.setSpacing(bSp)
3088
+ self.outerBox.setSpacing(4)
3141
3089
  self.outerBox.addWidget(self.statusIcon)
3142
3090
  self.outerBox.addWidget(self.statusText)
3143
3091
  self.outerBox.addStretch(1)
3144
3092
  self.outerBox.addWidget(self.linesIcon)
3145
3093
  self.outerBox.addWidget(self.linesText)
3146
- self.outerBox.addSpacing(hSp)
3094
+ self.outerBox.addSpacing(6)
3147
3095
  self.outerBox.addWidget(self.wordsIcon)
3148
3096
  self.outerBox.addWidget(self.wordsText)
3149
- self.outerBox.setContentsMargins(mPx, mPx, mPx, mPx)
3097
+ self.outerBox.setContentsMargins(8, 8, 8, 8)
3150
3098
 
3151
3099
  self.setLayout(self.outerBox)
3152
3100
 
3153
3101
  # Fix Margins and Size
3154
3102
  # This is needed for high DPI systems. See issue #499.
3155
3103
  self.setContentsMargins(0, 0, 0, 0)
3156
- self.setMinimumHeight(fPx + 2*mPx)
3104
+ self.setMinimumHeight(fPx + 16)
3157
3105
 
3158
3106
  # Fix the Colours
3159
3107
  self.updateFont()
@@ -3181,19 +3129,21 @@ class GuiDocEditFooter(QWidget):
3181
3129
  def updateTheme(self) -> None:
3182
3130
  """Update theme elements."""
3183
3131
  iPx = round(0.9*SHARED.theme.baseIconHeight)
3184
- self.linesIcon.setPixmap(SHARED.theme.getPixmap("status_lines", (iPx, iPx)))
3185
- self.wordsIcon.setPixmap(SHARED.theme.getPixmap("status_stats", (iPx, iPx)))
3186
- self.matchColours()
3132
+ self.linesIcon.setPixmap(SHARED.theme.getPixmap("lines", (iPx, iPx)))
3133
+ self.wordsIcon.setPixmap(SHARED.theme.getPixmap("stats", (iPx, iPx)))
3134
+ self.matchColors()
3187
3135
  return
3188
3136
 
3189
- def matchColours(self) -> None:
3137
+ def matchColors(self) -> None:
3190
3138
  """Update the colours of the widget to match those of the syntax
3191
3139
  theme rather than the main GUI.
3192
3140
  """
3193
- palette = QPalette()
3194
- palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
3195
- palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
3196
- palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
3141
+ syntax = SHARED.theme.syntaxTheme
3142
+
3143
+ palette = self.palette()
3144
+ palette.setColor(QPalette.ColorRole.Window, syntax.back)
3145
+ palette.setColor(QPalette.ColorRole.WindowText, syntax.text)
3146
+ palette.setColor(QPalette.ColorRole.Text, syntax.text)
3197
3147
 
3198
3148
  self.setPalette(palette)
3199
3149
  self.statusText.setPalette(palette)
@@ -3234,12 +3184,13 @@ class GuiDocEditFooter(QWidget):
3234
3184
 
3235
3185
  def updateLineCount(self, cursor: QTextCursor) -> None:
3236
3186
  """Update the line and document position counter."""
3237
- cPos = cursor.position() + 1
3238
- cLine = cursor.blockNumber() + 1
3239
- cCount = max(cursor.document().characterCount(), 1)
3240
- self.linesText.setText(
3241
- self._trLineCount.format(f"{cLine:n}", f"{100*cPos//cCount:d} %")
3242
- )
3187
+ if document := cursor.document():
3188
+ cPos = cursor.position() + 1
3189
+ cLine = cursor.blockNumber() + 1
3190
+ cCount = max(document.characterCount(), 1)
3191
+ self.linesText.setText(
3192
+ self._trLineCount.format(f"{cLine:n}", f"{100*cPos//cCount:d} %")
3193
+ )
3243
3194
  return
3244
3195
 
3245
3196
  def updateWordCount(self, wCount: int, selection: bool) -> None: