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.
- novelwriter/__init__.py +84 -74
- novelwriter/assets/i18n/nw_cs_CZ.qm +0 -0
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/icons/font_awesome.icons +109 -0
- novelwriter/assets/icons/material_filled_bold.icons +109 -0
- novelwriter/assets/icons/material_filled_normal.icons +109 -0
- novelwriter/assets/icons/material_filled_thin.icons +109 -0
- novelwriter/assets/icons/material_rounded_bold.icons +109 -0
- novelwriter/assets/icons/material_rounded_normal.icons +109 -0
- novelwriter/assets/icons/material_rounded_thin.icons +109 -0
- novelwriter/assets/icons/remix_filled.icons +108 -0
- novelwriter/assets/icons/remix_outline.icons +108 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/{manual_fr_FR.pdf → manual_fr.pdf} +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +1 -1
- novelwriter/assets/text/credits_en.htm +6 -6
- novelwriter/assets/themes/cyberpunk_night.conf +23 -7
- novelwriter/assets/themes/default_dark.conf +20 -4
- novelwriter/assets/themes/default_light.conf +21 -5
- novelwriter/assets/themes/dracula.conf +20 -4
- novelwriter/assets/themes/solarized_dark.conf +24 -8
- novelwriter/assets/themes/solarized_light.conf +22 -6
- novelwriter/common.py +33 -26
- novelwriter/config.py +118 -127
- novelwriter/constants.py +75 -56
- novelwriter/core/buildsettings.py +23 -16
- novelwriter/core/coretools.py +11 -7
- novelwriter/core/docbuild.py +19 -13
- novelwriter/core/document.py +2 -2
- novelwriter/core/index.py +142 -432
- novelwriter/core/indexdata.py +403 -0
- novelwriter/core/item.py +35 -28
- novelwriter/core/itemmodel.py +27 -26
- novelwriter/core/novelmodel.py +223 -0
- novelwriter/core/options.py +1 -1
- novelwriter/core/project.py +10 -11
- novelwriter/core/projectdata.py +5 -5
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +3 -2
- novelwriter/core/spellcheck.py +4 -3
- novelwriter/core/status.py +12 -15
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tree.py +46 -8
- novelwriter/dialogs/about.py +19 -22
- novelwriter/dialogs/docmerge.py +21 -23
- novelwriter/dialogs/docsplit.py +20 -23
- novelwriter/dialogs/editlabel.py +9 -13
- novelwriter/dialogs/preferences.py +111 -48
- novelwriter/dialogs/projectsettings.py +48 -54
- novelwriter/dialogs/quotes.py +14 -19
- novelwriter/dialogs/wordlist.py +25 -30
- novelwriter/enum.py +8 -0
- novelwriter/error.py +16 -16
- novelwriter/extensions/configlayout.py +18 -18
- novelwriter/extensions/eventfilters.py +9 -5
- novelwriter/extensions/modified.py +34 -15
- novelwriter/extensions/novelselector.py +18 -5
- novelwriter/extensions/pagedsidebar.py +39 -49
- novelwriter/extensions/progressbars.py +10 -8
- novelwriter/extensions/statusled.py +6 -13
- novelwriter/extensions/switch.py +19 -30
- novelwriter/extensions/switchbox.py +7 -3
- novelwriter/extensions/versioninfo.py +4 -4
- novelwriter/formats/shared.py +1 -1
- novelwriter/formats/todocx.py +14 -10
- novelwriter/formats/tohtml.py +7 -5
- novelwriter/formats/tokenizer.py +36 -33
- novelwriter/formats/tomarkdown.py +6 -2
- novelwriter/formats/toodt.py +27 -22
- novelwriter/formats/toqdoc.py +19 -14
- novelwriter/formats/toraw.py +6 -2
- novelwriter/gui/doceditor.py +216 -265
- novelwriter/gui/dochighlight.py +46 -45
- novelwriter/gui/docviewer.py +102 -104
- novelwriter/gui/docviewerpanel.py +47 -51
- novelwriter/gui/editordocument.py +8 -5
- novelwriter/gui/itemdetails.py +15 -18
- novelwriter/gui/mainmenu.py +147 -146
- novelwriter/gui/noveltree.py +239 -405
- novelwriter/gui/outline.py +137 -76
- novelwriter/gui/projtree.py +138 -132
- novelwriter/gui/search.py +33 -31
- novelwriter/gui/sidebar.py +13 -18
- novelwriter/gui/statusbar.py +13 -15
- novelwriter/gui/theme.py +533 -430
- novelwriter/guimain.py +27 -29
- novelwriter/shared.py +32 -23
- novelwriter/text/comments.py +70 -0
- novelwriter/text/patterns.py +4 -4
- novelwriter/tools/dictionaries.py +20 -29
- novelwriter/tools/lipsum.py +16 -17
- novelwriter/tools/manusbuild.py +39 -42
- novelwriter/tools/manuscript.py +113 -126
- novelwriter/tools/manussettings.py +78 -83
- novelwriter/tools/noveldetails.py +51 -64
- novelwriter/tools/welcome.py +56 -75
- novelwriter/tools/writingstats.py +44 -57
- novelwriter/types.py +5 -7
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/METADATA +6 -6
- novelwriter-2.7b1.dist-info/RECORD +159 -0
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/WHEEL +1 -1
- novelWriter-2.6.2.dist-info/RECORD +0 -363
- novelwriter/assets/icons/typicons_dark/README.md +0 -29
- novelwriter/assets/icons/typicons_dark/icons.conf +0 -134
- novelwriter/assets/icons/typicons_dark/mixed_copy.svg +0 -4
- novelwriter/assets/icons/typicons_dark/mixed_document-chapter.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_document-note.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-scene.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-section.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_document-title.svg +0 -12
- novelwriter/assets/icons/typicons_dark/mixed_edit.svg +0 -4
- novelwriter/assets/icons/typicons_dark/mixed_import.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_input-none.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg +0 -5
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_search-replace.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +0 -6
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_deco-h0.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h1.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h2.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h3.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-h4.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_deco-noveltree-more.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_font.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_panel.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-case.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-preserve.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-regex.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_search-word.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +0 -4
- novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +0 -6
- novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +0 -7
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_arrow-down-thick-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-forward.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-maximise.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-minimise.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_arrow-repeat-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_book-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_book.svg +0 -6
- novelwriter/assets/icons/typicons_dark/typ_bookmark.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_calendar.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_cancel-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_cancel.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chart-bar-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-down.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-left.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-right.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_chevron-up.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_cog.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_delete-full.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_delete.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_directions-full.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_document-add.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_document-text.svg +0 -8
- novelwriter/assets/icons/typicons_dark/typ_document.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_export-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_export.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_eye.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_flag.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_folder-open.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_folder.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_globe-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_key.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_lightbulb-full.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_location.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_media-pause-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_media-record-outline.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_media-record.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_minus.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_pencil.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_pin-outline.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_pin.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_plus.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_puzzle-outline.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_puzzle.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_refresh.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_search.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_star.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_stopwatch-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-list-grey.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_th-list.svg +0 -9
- novelwriter/assets/icons/typicons_dark/typ_times.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_trash.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +0 -4
- novelwriter/assets/icons/typicons_dark/typ_user.svg +0 -5
- novelwriter/assets/icons/typicons_dark/typ_warning-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/README.md +0 -29
- novelwriter/assets/icons/typicons_light/icons.conf +0 -134
- novelwriter/assets/icons/typicons_light/mixed_copy.svg +0 -4
- novelwriter/assets/icons/typicons_light/mixed_document-chapter.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-new.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_document-note.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-scene.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-section.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_document-title.svg +0 -12
- novelwriter/assets/icons/typicons_light/mixed_edit.svg +0 -4
- novelwriter/assets/icons/typicons_light/mixed_import.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_input-checked.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_input-none.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg +0 -5
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_search-replace.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +0 -6
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_deco-h0.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h1.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h2.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h3.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-h4.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-note.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_deco-noveltree-more.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_font.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_panel.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_quote.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-case.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-preserve.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-regex.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_search-word.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +0 -4
- novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +0 -6
- novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +0 -7
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_arrow-down-thick-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-forward.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-maximise.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-minimise.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_arrow-repeat-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_book-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_book.svg +0 -6
- novelwriter/assets/icons/typicons_light/typ_bookmark.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_calendar.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_cancel-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_cancel.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chart-bar-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-down.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-left.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-right.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_chevron-up.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_cog.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_delete-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_delete.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_directions-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_document-add.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_document-text.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_document.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_export-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_export.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_eye.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_flag.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_folder-open.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_folder.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_globe-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_key.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_lightbulb-full.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_location.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_media-pause-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_media-record-outline.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_media-record.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_minus.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_pencil.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_pin-outline.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_pin.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_plus.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_puzzle-outline.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_puzzle.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_refresh.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_search-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_search.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_star.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_stopwatch-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-list-grey.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_th-list.svg +0 -9
- novelwriter/assets/icons/typicons_light/typ_times.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_trash.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +0 -4
- novelwriter/assets/icons/typicons_light/typ_user.svg +0 -5
- novelwriter/assets/icons/typicons_light/typ_warning-full.svg +0 -4
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info/licenses}/LICENSE.md +0 -0
- {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/top_level.txt +0 -0
novelwriter/gui/doceditor.py
CHANGED
@@ -37,29 +37,31 @@ import logging
|
|
37
37
|
from enum import Enum, IntFlag
|
38
38
|
from time import time
|
39
39
|
|
40
|
-
from
|
40
|
+
from PyQt6.QtCore import (
|
41
41
|
QObject, QPoint, QRegularExpression, QRunnable, Qt, QTimer, pyqtSignal,
|
42
42
|
pyqtSlot
|
43
43
|
)
|
44
|
-
from
|
45
|
-
|
46
|
-
QKeySequence, QMouseEvent, QPalette, QPixmap, QResizeEvent,
|
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
|
50
|
-
|
51
|
-
|
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
|
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
|
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
|
-
"
|
99
|
-
"
|
100
|
-
"
|
101
|
-
"
|
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(
|
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.
|
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
|
296
|
+
def updateSyntaxColors(self) -> None:
|
296
297
|
"""Update the syntax highlighting theme."""
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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.
|
309
|
-
self.docFooter.
|
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.
|
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
|
367
|
-
self.setTabStopDistance(CONFIG.
|
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
|
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 (
|
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 (
|
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(
|
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(
|
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(
|
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
|
-
|
475
|
-
cC, wC, pC = standardCounter(
|
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(
|
481
|
+
if not self._nwDocument.writeDocument(text):
|
479
482
|
saveOk = False
|
480
|
-
if self._nwDocument.hashError
|
481
|
-
|
482
|
-
|
483
|
-
|
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
|
-
|
522
|
-
|
523
|
-
|
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
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
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-
|
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
|
-
|
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,
|
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.
|
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("
|
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
|
-
|
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 *
|
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(
|
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,
|
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("
|
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
|
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(
|
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
|
1132
|
-
|
1133
|
-
|
1134
|
-
cursor.setPosition(
|
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
|
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
|
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
|
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
|
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
|
1156
|
+
action = qtAddAction(ctxMenu, self.tr("Cut"))
|
1180
1157
|
action.triggered.connect(qtLambda(self.docAction, nwDocAction.CUT))
|
1181
|
-
action = ctxMenu
|
1158
|
+
action = qtAddAction(ctxMenu, self.tr("Copy"))
|
1182
1159
|
action.triggered.connect(qtLambda(self.docAction, nwDocAction.COPY))
|
1183
1160
|
|
1184
|
-
action = ctxMenu
|
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
|
1166
|
+
action = qtAddAction(ctxMenu, self.tr("Select All"))
|
1190
1167
|
action.triggered.connect(qtLambda(self.docAction, nwDocAction.SEL_ALL))
|
1191
|
-
action = ctxMenu
|
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
|
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
|
1188
|
+
qtAddAction(ctxMenu, self.tr("Spelling Suggestion(s)"))
|
1212
1189
|
for option in suggest[:15]:
|
1213
|
-
action = ctxMenu
|
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
|
1194
|
+
qtAddAction(ctxMenu, f"{nwUnicode.U_ENDASH} {trNone}")
|
1218
1195
|
|
1219
1196
|
ctxMenu.addSeparator()
|
1220
|
-
action = ctxMenu
|
1197
|
+
action = qtAddAction(ctxMenu, self.tr("Ignore Word"))
|
1221
1198
|
action.triggered.connect(qtLambda(self._addWord, word, block, False))
|
1222
|
-
action = ctxMenu
|
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
|
-
|
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
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
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
|
-
|
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
|
-
|
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
|
1531
|
+
for _ in range(fLen):
|
1556
1532
|
cursor.deletePreviousChar()
|
1557
1533
|
cursor.setPosition(posE)
|
1558
|
-
for
|
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(
|
1573
|
-
|
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(
|
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(
|
2052
|
-
|
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
|
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
|
-
"
|
2219
|
-
"
|
2220
|
-
"
|
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(
|
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(
|
2437
|
-
self.outerBox.setSpacing(
|
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
|
-
|
2452
|
-
|
2453
|
-
palette.
|
2454
|
-
palette.setColor(QPalette.ColorRole.
|
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
|
2458
|
-
self.tbItalicMD.setThemeIcon("fmt_italic
|
2459
|
-
self.tbStrikeMD.setThemeIcon("fmt_strike
|
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(
|
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(
|
2590
|
-
self.mainBox.setContentsMargins(
|
2567
|
+
self.mainBox.setSpacing(2)
|
2568
|
+
self.mainBox.setContentsMargins(6, 6, 6, 6)
|
2591
2569
|
|
2592
|
-
|
2593
|
-
self.
|
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
|
-
|
2697
|
-
|
2698
|
-
self.
|
2699
|
-
self.
|
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.
|
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
|
-
|
2835
|
-
|
2836
|
-
|
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 =
|
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(
|
2871
|
+
self.outerBox.addSpacing(4)
|
2910
2872
|
self.outerBox.addWidget(self.itemTitle, 1)
|
2911
|
-
self.outerBox.addSpacing(
|
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(
|
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 +
|
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
|
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("
|
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.
|
2948
|
+
self.matchColors()
|
2987
2949
|
|
2988
2950
|
return
|
2989
2951
|
|
2990
|
-
def
|
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
|
-
|
2995
|
-
palette.
|
2996
|
-
palette.setColor(QPalette.ColorRole.
|
2997
|
-
palette.setColor(QPalette.ColorRole.
|
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(
|
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(
|
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(
|
3094
|
+
self.outerBox.addSpacing(6)
|
3147
3095
|
self.outerBox.addWidget(self.wordsIcon)
|
3148
3096
|
self.outerBox.addWidget(self.wordsText)
|
3149
|
-
self.outerBox.setContentsMargins(
|
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 +
|
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("
|
3185
|
-
self.wordsIcon.setPixmap(SHARED.theme.getPixmap("
|
3186
|
-
self.
|
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
|
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
|
-
|
3194
|
-
|
3195
|
-
palette.
|
3196
|
-
palette.setColor(QPalette.ColorRole.
|
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
|
-
|
3238
|
-
|
3239
|
-
|
3240
|
-
|
3241
|
-
self.
|
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:
|