Merge branch 'main' into xml_rw_class
@@ -14,7 +14,7 @@ jobs:
|
|||||||
testLinux:
|
testLinux:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Python Setup
|
- name: Python Setup
|
||||||
|
|||||||
@@ -229,11 +229,28 @@ final release.
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
## Version 1.6.6 [2022-10-25]
|
||||||
|
|
||||||
|
### Release Notes
|
||||||
|
|
||||||
|
This is a bugfix release that fixes a minor issues with following tags in the editor. It is now
|
||||||
|
possible to also follow tags that contain spaces.
|
||||||
|
|
||||||
|
### Detailed Changelog
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
* Fix a bug where only the word under the cursor would be looked up when the user tried to follow a
|
||||||
|
tag in the editor. The lookup function now uses the same parser for the `@`-line as the syntax
|
||||||
|
highlighter does, so they should behave consistently. Issue #1195. PR #1209.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
## Version 1.6.5 [2022-10-13]
|
## Version 1.6.5 [2022-10-13]
|
||||||
|
|
||||||
### Release Notes
|
### Release Notes
|
||||||
|
|
||||||
This is a bugfix release that fixes a a few minor issues. The idle time for new projects would be
|
This is a bugfix release that fixes a few minor issues. The idle time for new projects would be
|
||||||
artificially inflated as the clock was not reset when the project was first created. This only
|
artificially inflated as the clock was not reset when the project was first created. This only
|
||||||
affects the first entry in the writing statistics. A scaling issue for the Preferences dialog has
|
affects the first entry in the writing statistics. A scaling issue for the Preferences dialog has
|
||||||
also been fixed. It only affected screens with UI scaling enabled. Lastly, typing `Shift+Enter` in
|
also been fixed. It only affected screens with UI scaling enabled. Lastly, typing `Shift+Enter` in
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ a key or key combination for the inserted content.
|
|||||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`F`", "Insert a ``@focus`` keyword."
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`F`", "Insert a ``@focus`` keyword."
|
||||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`C`", "Insert a ``@char`` keyword."
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`C`", "Insert a ``@char`` keyword."
|
||||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`P`", "Insert a ``@plot`` keyword."
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`P`", "Insert a ``@plot`` keyword."
|
||||||
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`S`", "Insert a synopsis comment."
|
||||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`T`", "Insert a ``@time`` keyword."
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`T`", "Insert a ``@time`` keyword."
|
||||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`L`", "Insert a ``@location`` keyword."
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`L`", "Insert a ``@location`` keyword."
|
||||||
":kbd:`Ctrl`:kbd:`K`, :kbd:`O`", "Insert an ``@object`` keyword."
|
":kbd:`Ctrl`:kbd:`K`, :kbd:`O`", "Insert an ``@object`` keyword."
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ backward = typ_chevron-left.svg
|
|||||||
bookmark = typ_bookmark.svg
|
bookmark = typ_bookmark.svg
|
||||||
bullet-off = typ_media-record-outline.svg
|
bullet-off = typ_media-record-outline.svg
|
||||||
bullet-on = typ_media-record.svg
|
bullet-on = typ_media-record.svg
|
||||||
check = typ_tick.svg
|
checked = mixed_input-checked.svg
|
||||||
close = typ_times.svg
|
close = typ_times.svg
|
||||||
cls_archive = typ_delete.svg
|
cls_archive = typ_delete.svg
|
||||||
cls_character = typ_user.svg
|
cls_character = typ_user.svg
|
||||||
@@ -41,13 +41,14 @@ forward = typ_chevron-right.svg
|
|||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-menu.svg
|
||||||
minimise = typ_arrow-minimise.svg
|
minimise = typ_arrow-minimise.svg
|
||||||
|
noncheckable = mixed_input-none.svg
|
||||||
proj_chapter = mixed_document-chapter.svg
|
proj_chapter = mixed_document-chapter.svg
|
||||||
proj_details = typ_th-list-grey.svg
|
proj_details = typ_th-list-grey.svg
|
||||||
proj_document = typ_document-text.svg
|
proj_document = typ_document-text.svg
|
||||||
proj_folder = typ_folder.svg
|
proj_folder = typ_folder.svg
|
||||||
proj_note = mixed_document-note.svg
|
proj_note = mixed_document-note.svg
|
||||||
proj_section = mixed_document-section.svg
|
|
||||||
proj_scene = mixed_document-scene.svg
|
proj_scene = mixed_document-scene.svg
|
||||||
|
proj_section = mixed_document-section.svg
|
||||||
proj_stats = typ_chart-bar-grey.svg
|
proj_stats = typ_chart-bar-grey.svg
|
||||||
proj_title = mixed_document-title.svg
|
proj_title = mixed_document-title.svg
|
||||||
reference = typ_at.svg
|
reference = typ_at.svg
|
||||||
@@ -70,6 +71,7 @@ status_stats = typ_chart-bar-grey.svg
|
|||||||
status_time = typ_stopwatch-grey.svg
|
status_time = typ_stopwatch-grey.svg
|
||||||
sticky-off = typ_pin-outline.svg
|
sticky-off = typ_pin-outline.svg
|
||||||
sticky-on = typ_pin.svg
|
sticky-on = typ_pin.svg
|
||||||
|
unchecked = mixed_input-unchecked.svg
|
||||||
up = typ_chevron-up.svg
|
up = typ_chevron-up.svg
|
||||||
view_build = typ_export.svg
|
view_build = typ_export.svg
|
||||||
view_editor = mixed_edit.svg
|
view_editor = mixed_edit.svg
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg2287">
|
||||||
|
<defs
|
||||||
|
id="defs2291" />
|
||||||
|
<metadata
|
||||||
|
id="metadata2281">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
d="m17.714 22h-11.429c-2.3629 0-4.2857-1.9229-4.2857-4.2857v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h7.1429c0.79 0 1.4286 0.64 1.4286 1.4286 0 0.78857-0.63857 1.4286-1.4286 1.4286h-7.1429c-0.78857 0-1.4286 0.64143-1.4286 1.4286v11.429c0 0.78714 0.64 1.4286 1.4286 1.4286h11.429c0.78857 0 1.4286-0.64143 1.4286-1.4286v-4.2857c0-0.78857 0.63857-1.4286 1.4286-1.4286s1.4286 0.64 1.4286 1.4286v4.2857c0 2.3629-1.9229 4.2857-4.2857 4.2857z"
|
||||||
|
fill="#848484"
|
||||||
|
stroke-width="1.4286"
|
||||||
|
id="path2283" />
|
||||||
|
<path
|
||||||
|
d="m19.02 3.3503c-0.60933 0.010623-1.2003 0.31545-1.5586 0.86719l-5.0977 7.8477-2.3672-3.6445c-0.57329-0.88278-1.7442-1.1319-2.627-0.55859-0.88278 0.57329-1.1319 1.7461-0.55859 2.6289l3.9199 6.0391c0.38514 0.59307 1.0409 0.88965 1.6973 0.85352 0.03023-2.78e-4 0.05972-0.0041 0.08984-0.0059 0.0556-0.0057 0.11074-0.0088 0.16602-0.01953 0.52104-0.07621 1.0077-0.36147 1.3184-0.83984l6.6445-10.23c0.57329-0.88278 0.32419-2.0556-0.55859-2.6289-0.33104-0.21498-0.70276-0.31497-1.0684-0.30859z"
|
||||||
|
fill="#9c9"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.111"
|
||||||
|
id="path2285" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg2931">
|
||||||
|
<defs
|
||||||
|
id="defs2935" />
|
||||||
|
<metadata
|
||||||
|
id="metadata2925">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
d="m2 17.714v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h11.429c2.3629 0 4.2857 1.9229 4.2857 4.2857v7.1429c0 0.79-0.64 1.4286-1.4286 1.4286-0.78857 0-1.4286-0.63857-1.4286-1.4286v-7.1429c0-0.78857-0.64143-1.4286-1.4286-1.4286h-11.429c-0.78714 0-1.4286 0.64-1.4286 1.4286v11.429c0 0.78857 0.64143 1.4286 1.4286 1.4286h4.2857c0.78857 0 1.4286 0.63857 1.4286 1.4286s-0.64 1.4286-1.4286 1.4286h-4.2857c-2.3629 0-4.2857-1.9229-4.2857-4.2857zm15.714 4.2857h-11.429c-2.3629 0-4.2857-1.9229-4.2857-4.2857v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h7.1429c0.79 0 1.4286 0.64 1.4286 1.4286 0 0.78857-0.63857 1.4286-1.4286 1.4286h-7.1429c-0.78857 0-1.4286 0.64143-1.4286 1.4286v11.429c0 0.78714 0.64 1.4286 1.4286 1.4286h11.429c0.78857 0 1.4286-0.64143 1.4286-1.4286v-4.2857c0-0.78857 0.63857-1.4286 1.4286-1.4286s1.4286 0.64 1.4286 1.4286v4.2857c0 2.3629-1.9229 4.2857-4.2857 4.2857z"
|
||||||
|
fill="#848484"
|
||||||
|
stroke-width="1.4286"
|
||||||
|
id="path2927" />
|
||||||
|
<rect
|
||||||
|
x="7"
|
||||||
|
y="10.1"
|
||||||
|
width="10"
|
||||||
|
height="3.8"
|
||||||
|
rx="1.9"
|
||||||
|
ry="1.9"
|
||||||
|
fill="#f99157"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.111"
|
||||||
|
id="rect2929" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg3574">
|
||||||
|
<defs
|
||||||
|
id="defs3578" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3568">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
d="m17.714 22h-11.429c-2.3629 0-4.2857-1.9229-4.2857-4.2857v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h7.1429c0.79 0 1.4286 0.64 1.4286 1.4286 0 0.78857-0.63857 1.4286-1.4286 1.4286h-7.1429c-0.78857 0-1.4286 0.64143-1.4286 1.4286v11.429c0 0.78714 0.64 1.4286 1.4286 1.4286h11.429c0.78857 0 1.4286-0.64143 1.4286-1.4286v-4.2857c0-0.78857 0.63857-1.4286 1.4286-1.4286s1.4286 0.64 1.4286 1.4286v4.2857c0 2.3629-1.9229 4.2857-4.2857 4.2857z"
|
||||||
|
fill="#848484"
|
||||||
|
stroke-width="1.4286"
|
||||||
|
id="path3570" />
|
||||||
|
<path
|
||||||
|
d="m17.99 3.3379c-0.48501 0.025418-0.96034 0.23584-1.3125 0.62695l-4.6777 5.1953-1.332-1.4785c-0.70433-0.78223-1.9014-0.84495-2.6836-0.14062-0.78223 0.70433-0.84495 1.9014-0.14062 2.6836l1.5996 1.7754-1.5977 1.7754c-0.70433 0.78223-0.64161 1.9793 0.14062 2.6836 0.78223 0.70433 1.9773 0.64161 2.6816-0.14062l1.332-1.4785 1.332 1.4785c0.70433 0.78224 1.9014 0.84495 2.6836 0.14062 0.78223-0.70433 0.84495-1.9014 0.14062-2.6836l-1.5996-1.7754 4.9453-5.4922c0.70433-0.78223 0.64161-1.9793-0.14062-2.6836-0.39112-0.35216-0.88608-0.51175-1.3711-0.48633z"
|
||||||
|
fill="#d64848"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.111"
|
||||||
|
id="path3572" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg896">
|
|
||||||
<metadata
|
|
||||||
id="metadata902">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs900" />
|
|
||||||
<path
|
|
||||||
d="M 19.576035,3.3487939 C 18.237131,2.6038813 16.550693,3.0884898 15.809935,4.4246248 L 10.66893,13.676495 7.726664,10.734229 c -1.0813694,-1.0813704 -2.8342677,-1.0813704 -3.9156371,0 -1.0813693,1.081369 -1.0813693,2.834267 0,3.915637 l 5.538383,5.538383 c 0.523378,0.524762 1.2295221,0.812758 1.9578191,0.812758 l 0.383533,-0.02769 c 0.859834,-0.12046 1.614439,-0.636914 2.03674,-1.397057 L 20.650482,7.1148966 C 21.39401,5.777375 20.91217,4.0923218 19.576035,3.3487939 Z"
|
|
||||||
id="path894"
|
|
||||||
style="stroke-width:1.3846;fill:#99cc99;fill-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -21,7 +21,7 @@ backward = typ_chevron-left.svg
|
|||||||
bookmark = typ_bookmark.svg
|
bookmark = typ_bookmark.svg
|
||||||
bullet-off = typ_media-record-outline.svg
|
bullet-off = typ_media-record-outline.svg
|
||||||
bullet-on = typ_media-record.svg
|
bullet-on = typ_media-record.svg
|
||||||
check = typ_tick.svg
|
checked = mixed_input-checked.svg
|
||||||
close = typ_times.svg
|
close = typ_times.svg
|
||||||
cls_archive = typ_delete.svg
|
cls_archive = typ_delete.svg
|
||||||
cls_character = typ_user.svg
|
cls_character = typ_user.svg
|
||||||
@@ -41,13 +41,14 @@ forward = typ_chevron-right.svg
|
|||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-menu.svg
|
||||||
minimise = typ_arrow-minimise.svg
|
minimise = typ_arrow-minimise.svg
|
||||||
|
noncheckable = mixed_input-none.svg
|
||||||
proj_chapter = mixed_document-chapter.svg
|
proj_chapter = mixed_document-chapter.svg
|
||||||
proj_details = typ_th-list-grey.svg
|
proj_details = typ_th-list-grey.svg
|
||||||
proj_document = typ_document-text.svg
|
proj_document = typ_document-text.svg
|
||||||
proj_folder = typ_folder.svg
|
proj_folder = typ_folder.svg
|
||||||
proj_note = mixed_document-note.svg
|
proj_note = mixed_document-note.svg
|
||||||
proj_section = mixed_document-section.svg
|
|
||||||
proj_scene = mixed_document-scene.svg
|
proj_scene = mixed_document-scene.svg
|
||||||
|
proj_section = mixed_document-section.svg
|
||||||
proj_stats = typ_chart-bar-grey.svg
|
proj_stats = typ_chart-bar-grey.svg
|
||||||
proj_title = mixed_document-title.svg
|
proj_title = mixed_document-title.svg
|
||||||
reference = typ_at.svg
|
reference = typ_at.svg
|
||||||
@@ -70,6 +71,7 @@ status_stats = typ_chart-bar-grey.svg
|
|||||||
status_time = typ_stopwatch-grey.svg
|
status_time = typ_stopwatch-grey.svg
|
||||||
sticky-off = typ_pin-outline.svg
|
sticky-off = typ_pin-outline.svg
|
||||||
sticky-on = typ_pin.svg
|
sticky-on = typ_pin.svg
|
||||||
|
unchecked = mixed_input-unchecked.svg
|
||||||
up = typ_chevron-up.svg
|
up = typ_chevron-up.svg
|
||||||
view_build = typ_export.svg
|
view_build = typ_export.svg
|
||||||
view_editor = mixed_edit.svg
|
view_editor = mixed_edit.svg
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg879">
|
||||||
|
<defs
|
||||||
|
id="defs883" />
|
||||||
|
<metadata
|
||||||
|
id="metadata873">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
d="m17.714 22h-11.429c-2.3629 0-4.2857-1.9229-4.2857-4.2857v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h7.1429c0.79 0 1.4286 0.64 1.4286 1.4286 0 0.78857-0.63857 1.4286-1.4286 1.4286h-7.1429c-0.78857 0-1.4286 0.64143-1.4286 1.4286v11.429c0 0.78714 0.64 1.4286 1.4286 1.4286h11.429c0.78857 0 1.4286-0.64143 1.4286-1.4286v-4.2857c0-0.78857 0.63857-1.4286 1.4286-1.4286s1.4286 0.64 1.4286 1.4286v4.2857c0 2.3629-1.9229 4.2857-4.2857 4.2857z"
|
||||||
|
fill="#333"
|
||||||
|
stroke-width="1.4286"
|
||||||
|
id="path875" />
|
||||||
|
<path
|
||||||
|
d="m19.02 3.3503c-0.60933 0.010623-1.2003 0.31545-1.5586 0.86719l-5.0977 7.8477-2.3672-3.6445c-0.57329-0.88278-1.7442-1.1319-2.627-0.55859-0.88278 0.57329-1.1319 1.7461-0.55859 2.6289l3.9199 6.0391c0.38514 0.59307 1.0409 0.88965 1.6973 0.85352 0.03023-2.78e-4 0.05972-0.0041 0.08984-0.0059 0.0556-0.0057 0.11074-0.0088 0.16602-0.01953 0.52104-0.07621 1.0077-0.36147 1.3184-0.83984l6.6445-10.23c0.57329-0.88278 0.32419-2.0556-0.55859-2.6289-0.33104-0.21498-0.70276-0.31497-1.0684-0.30859z"
|
||||||
|
fill="#718c00"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.111"
|
||||||
|
id="path877" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg973">
|
||||||
|
<defs
|
||||||
|
id="defs977" />
|
||||||
|
<metadata
|
||||||
|
id="metadata967">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
d="m2 17.714v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h11.429c2.3629 0 4.2857 1.9229 4.2857 4.2857v7.1429c0 0.79-0.64 1.4286-1.4286 1.4286-0.78857 0-1.4286-0.63857-1.4286-1.4286v-7.1429c0-0.78857-0.64143-1.4286-1.4286-1.4286h-11.429c-0.78714 0-1.4286 0.64-1.4286 1.4286v11.429c0 0.78857 0.64143 1.4286 1.4286 1.4286h4.2857c0.78857 0 1.4286 0.63857 1.4286 1.4286s-0.64 1.4286-1.4286 1.4286h-4.2857c-2.3629 0-4.2857-1.9229-4.2857-4.2857zm15.714 4.2857h-11.429c-2.3629 0-4.2857-1.9229-4.2857-4.2857v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h7.1429c0.79 0 1.4286 0.64 1.4286 1.4286 0 0.78857-0.63857 1.4286-1.4286 1.4286h-7.1429c-0.78857 0-1.4286 0.64143-1.4286 1.4286v11.429c0 0.78714 0.64 1.4286 1.4286 1.4286h11.429c0.78857 0 1.4286-0.64143 1.4286-1.4286v-4.2857c0-0.78857 0.63857-1.4286 1.4286-1.4286s1.4286 0.64 1.4286 1.4286v4.2857c0 2.3629-1.9229 4.2857-4.2857 4.2857z"
|
||||||
|
fill="#333"
|
||||||
|
stroke-width="1.4286"
|
||||||
|
id="path969" />
|
||||||
|
<rect
|
||||||
|
x="7"
|
||||||
|
y="10.1"
|
||||||
|
width="10"
|
||||||
|
height="3.8"
|
||||||
|
rx="1.9"
|
||||||
|
ry="1.9"
|
||||||
|
fill="#f5871f"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.111"
|
||||||
|
id="rect971" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.2"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
id="svg1616">
|
||||||
|
<defs
|
||||||
|
id="defs1620" />
|
||||||
|
<metadata
|
||||||
|
id="metadata1610">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
d="m17.714 22h-11.429c-2.3629 0-4.2857-1.9229-4.2857-4.2857v-11.429c0-2.3629 1.9229-4.2857 4.2857-4.2857h7.1429c0.79 0 1.4286 0.64 1.4286 1.4286 0 0.78857-0.63857 1.4286-1.4286 1.4286h-7.1429c-0.78857 0-1.4286 0.64143-1.4286 1.4286v11.429c0 0.78714 0.64 1.4286 1.4286 1.4286h11.429c0.78857 0 1.4286-0.64143 1.4286-1.4286v-4.2857c0-0.78857 0.63857-1.4286 1.4286-1.4286s1.4286 0.64 1.4286 1.4286v4.2857c0 2.3629-1.9229 4.2857-4.2857 4.2857z"
|
||||||
|
fill="#333"
|
||||||
|
stroke-width="1.4286"
|
||||||
|
id="path1612" />
|
||||||
|
<path
|
||||||
|
d="m17.99 3.3379c-0.48501 0.025418-0.96034 0.23584-1.3125 0.62695l-4.6777 5.1953-1.332-1.4785c-0.70433-0.78223-1.9014-0.84495-2.6836-0.14062-0.78223 0.70433-0.84495 1.9014-0.14062 2.6836l1.5996 1.7754-1.5977 1.7754c-0.70433 0.78223-0.64161 1.9793 0.14062 2.6836 0.78223 0.70433 1.9773 0.64161 2.6816-0.14062l1.332-1.4785 1.332 1.4785c0.70433 0.78224 1.9014 0.84495 2.6836 0.14062 0.78223-0.70433 0.84495-1.9014 0.14062-2.6836l-1.5996-1.7754 4.9453-5.4922c0.70433-0.78223 0.64161-1.9793-0.14062-2.6836-0.39112-0.35216-0.88608-0.51175-1.3711-0.48633z"
|
||||||
|
fill="#c82829"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.111"
|
||||||
|
id="path1614" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
version="1.2"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
id="svg896">
|
|
||||||
<metadata
|
|
||||||
id="metadata902">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs900" />
|
|
||||||
<path
|
|
||||||
d="M 19.576035,3.3487939 C 18.237131,2.6038813 16.550693,3.0884898 15.809935,4.4246248 L 10.66893,13.676495 7.726664,10.734229 c -1.0813694,-1.0813704 -2.8342677,-1.0813704 -3.9156371,0 -1.0813693,1.081369 -1.0813693,2.834267 0,3.915637 l 5.538383,5.538383 c 0.523378,0.524762 1.2295221,0.812758 1.9578191,0.812758 l 0.383533,-0.02769 c 0.859834,-0.12046 1.614439,-0.636914 2.03674,-1.397057 L 20.650482,7.1148966 C 21.39401,5.777375 20.91217,4.0923218 19.576035,3.3487939 Z"
|
|
||||||
id="path894"
|
|
||||||
style="stroke-width:1.3846;fill:#718c00;fill-opacity:1" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,2 +1,4 @@
|
|||||||
[Main]
|
[Main]
|
||||||
name = Default System Theme
|
name = Default Theme
|
||||||
|
description = Qt standard colours
|
||||||
|
icontheme = typicons_light
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
[Main]
|
[Main]
|
||||||
name = Default Dark Theme
|
name = Default Dark Theme
|
||||||
author = Veronica Berglyd Olsen
|
description = The novelWriter standard dark theme
|
||||||
credit = Veronica Berglyd Olsen
|
author = Veronica Berglyd Olsen
|
||||||
url = https://github.com/vkbo/novelWriter
|
credit = Veronica Berglyd Olsen
|
||||||
license = CC BY-SA 4.0
|
url = https://github.com/vkbo/novelWriter
|
||||||
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
license = CC BY-SA 4.0
|
||||||
|
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
icontheme = typicons_dark
|
||||||
|
|
||||||
[Palette]
|
[Palette]
|
||||||
window = 54, 54, 54
|
window = 54, 54, 54
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
/**
|
|
||||||
* Default Theme: Dark
|
|
||||||
* This theme doesn't use any custom styles, so the file is only here as
|
|
||||||
* an example. There doesn't have to be a styles.qss file in the folder.
|
|
||||||
*/
|
|
||||||
@@ -5,6 +5,7 @@ credit = Ethan Schoonover
|
|||||||
url = https://ethanschoonover.com/solarized/
|
url = https://ethanschoonover.com/solarized/
|
||||||
license = MIT
|
license = MIT
|
||||||
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||||
|
icontheme = typicons_dark
|
||||||
|
|
||||||
[Palette]
|
[Palette]
|
||||||
window = 0, 43, 54
|
window = 0, 43, 54
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ credit = Ethan Schoonover
|
|||||||
url = https://ethanschoonover.com/solarized/
|
url = https://ethanschoonover.com/solarized/
|
||||||
license = MIT
|
license = MIT
|
||||||
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||||
|
icontheme = typicons_light
|
||||||
|
|
||||||
[Palette]
|
[Palette]
|
||||||
window = 238, 232, 213
|
window = 238, 232, 213
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ class Config:
|
|||||||
# General
|
# General
|
||||||
self.guiTheme = "" # GUI theme
|
self.guiTheme = "" # GUI theme
|
||||||
self.guiSyntax = "" # Syntax theme
|
self.guiSyntax = "" # Syntax theme
|
||||||
self.guiIcons = "" # Icon theme
|
|
||||||
self.guiFont = "" # Defaults to system default font
|
self.guiFont = "" # Defaults to system default font
|
||||||
self.guiFontSize = 11 # Is overridden if system default is loaded
|
self.guiFontSize = 11 # Is overridden if system default is loaded
|
||||||
self.guiScale = 1.0 # Set automatically by Theme class
|
self.guiScale = 1.0 # Set automatically by Theme class
|
||||||
@@ -81,7 +80,6 @@ class Config:
|
|||||||
|
|
||||||
self.setDefaultGuiTheme()
|
self.setDefaultGuiTheme()
|
||||||
self.setDefaultSyntaxTheme()
|
self.setDefaultSyntaxTheme()
|
||||||
self.setDefaultIconTheme()
|
|
||||||
|
|
||||||
# Localisation
|
# Localisation
|
||||||
self.qLocal = QLocale.system()
|
self.qLocal = QLocale.system()
|
||||||
@@ -410,7 +408,6 @@ class Config:
|
|||||||
cnfSec = "Main"
|
cnfSec = "Main"
|
||||||
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
|
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
|
||||||
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
|
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
|
||||||
self.guiIcons = theConf.rdStr(cnfSec, "icons", self.guiIcons)
|
|
||||||
self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
|
self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
|
||||||
self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
|
self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
|
||||||
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
|
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
|
||||||
@@ -508,12 +505,6 @@ class Config:
|
|||||||
logger.info("Using straight double quotes, so disabling auto-replace")
|
logger.info("Using straight double quotes, so disabling auto-replace")
|
||||||
self.doReplaceDQuote = False
|
self.doReplaceDQuote = False
|
||||||
|
|
||||||
# Check deprecated settings
|
|
||||||
if self.guiIcons in ("typicons_colour_dark", "typicons_grey_dark"):
|
|
||||||
self.guiIcons = "typicons_dark"
|
|
||||||
elif self.guiIcons in ("typicons_colour_light", "typicons_grey_light"):
|
|
||||||
self.guiIcons = "typicons_light"
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveConfig(self):
|
def saveConfig(self):
|
||||||
@@ -529,7 +520,6 @@ class Config:
|
|||||||
"timestamp": formatTimeStamp(time()),
|
"timestamp": formatTimeStamp(time()),
|
||||||
"theme": str(self.guiTheme),
|
"theme": str(self.guiTheme),
|
||||||
"syntax": str(self.guiSyntax),
|
"syntax": str(self.guiSyntax),
|
||||||
"icons": str(self.guiIcons),
|
|
||||||
"guifont": str(self.guiFont),
|
"guifont": str(self.guiFont),
|
||||||
"guifontsize": str(self.guiFontSize),
|
"guifontsize": str(self.guiFontSize),
|
||||||
"lastnotes": str(self.lastNotes),
|
"lastnotes": str(self.lastNotes),
|
||||||
@@ -816,11 +806,6 @@ class Config:
|
|||||||
"""
|
"""
|
||||||
self.guiSyntax = "default_light"
|
self.guiSyntax = "default_light"
|
||||||
|
|
||||||
def setDefaultIconTheme(self):
|
|
||||||
"""Reset the icon theme to default value.
|
|
||||||
"""
|
|
||||||
self.guiIcons = "typicons_light"
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Getters
|
# Getters
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class DocSplitter:
|
|||||||
"""An iterator that will write each document in the buffer, and
|
"""An iterator that will write each document in the buffer, and
|
||||||
return its new handle, parent handle, and sibling handle.
|
return its new handle, parent handle, and sibling handle.
|
||||||
"""
|
"""
|
||||||
if self._srcHandle is None:
|
if self._srcHandle is None or self._srcItem is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
pHandle = self._parHandle
|
pHandle = self._parHandle
|
||||||
@@ -224,6 +224,10 @@ class DocSplitter:
|
|||||||
dHandle = self.theProject.newFile(docLabel, pHandle)
|
dHandle = self.theProject.newFile(docLabel, pHandle)
|
||||||
hHandle[hLevel] = dHandle
|
hHandle[hLevel] = dHandle
|
||||||
|
|
||||||
|
newItem = self.theProject.tree[dHandle]
|
||||||
|
newItem.setStatus(self._srcItem.itemStatus)
|
||||||
|
newItem.setImport(self._srcItem.itemImport)
|
||||||
|
|
||||||
outDoc = NWDoc(self.theProject, dHandle)
|
outDoc = NWDoc(self.theProject, dHandle)
|
||||||
status = outDoc.writeDocument("\n".join(docText))
|
status = outDoc.writeDocument("\n".join(docText))
|
||||||
if not status:
|
if not status:
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ import novelwriter
|
|||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
||||||
|
from PyQt5.QtCore import QRectF, Qt
|
||||||
|
|
||||||
from novelwriter.common import checkInt, minmax, simplified
|
from novelwriter.common import checkInt, minmax, simplified
|
||||||
|
|
||||||
@@ -49,10 +50,15 @@ class NWStatus:
|
|||||||
self._reverse = {}
|
self._reverse = {}
|
||||||
self._default = None
|
self._default = None
|
||||||
|
|
||||||
self._iconSize = novelwriter.CONFIG.pxInt(32)
|
self._iPX = novelwriter.CONFIG.pxInt(24)
|
||||||
pixmap = QPixmap(self._iconSize, self._iconSize)
|
|
||||||
pixmap.fill(QColor(100, 100, 100))
|
pA = novelwriter.CONFIG.pxInt(2)
|
||||||
self._defaultIcon = QIcon(pixmap)
|
pB = novelwriter.CONFIG.pxInt(20)
|
||||||
|
pR = float(novelwriter.CONFIG.pxInt(4))
|
||||||
|
self._iconPath = QPainterPath()
|
||||||
|
self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR)
|
||||||
|
|
||||||
|
self._defaultIcon = self._createIcon([100, 100, 100])
|
||||||
|
|
||||||
if self._type == self.STATUS:
|
if self._type == self.STATUS:
|
||||||
self._prefix = "s"
|
self._prefix = "s"
|
||||||
@@ -63,19 +69,16 @@ class NWStatus:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def write(self, key, name, cols, count=None):
|
def write(self, key, name, col, count=None):
|
||||||
"""Add or update a status entry. If the key is invalid, a new
|
"""Add or update a status entry. If the key is invalid, a new
|
||||||
key is generated.
|
key is generated.
|
||||||
"""
|
"""
|
||||||
if not self._isKey(key):
|
if not self._isKey(key):
|
||||||
key = self._newKey()
|
key = self._newKey()
|
||||||
if not isinstance(cols, tuple):
|
if not isinstance(col, tuple):
|
||||||
cols = (100, 100, 100)
|
col = (100, 100, 100)
|
||||||
if len(cols) != 3:
|
if len(col) != 3:
|
||||||
cols = (100, 100, 100)
|
col = (100, 100, 100)
|
||||||
|
|
||||||
pixmap = QPixmap(self._iconSize, self._iconSize)
|
|
||||||
pixmap.fill(QColor(*cols))
|
|
||||||
|
|
||||||
name = simplified(name)
|
name = simplified(name)
|
||||||
if count is None:
|
if count is None:
|
||||||
@@ -83,8 +86,8 @@ class NWStatus:
|
|||||||
|
|
||||||
self._store[key] = {
|
self._store[key] = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"icon": QIcon(pixmap),
|
"icon": self._createIcon(col),
|
||||||
"cols": cols,
|
"cols": col,
|
||||||
"count": count,
|
"count": count,
|
||||||
}
|
}
|
||||||
self._reverse[name] = key
|
self._reverse[name] = key
|
||||||
@@ -267,6 +270,19 @@ class NWStatus:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _createIcon(self, col):
|
||||||
|
"""Generate an icon for a status label.
|
||||||
|
"""
|
||||||
|
pixmap = QPixmap(self._iPX, self._iPX)
|
||||||
|
pixmap.fill(Qt.transparent)
|
||||||
|
|
||||||
|
painter = QPainter(pixmap)
|
||||||
|
painter.setRenderHint(QPainter.Antialiasing)
|
||||||
|
painter.fillPath(self._iconPath, QColor(*col))
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
return QIcon(pixmap)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Iterator Bits
|
# Iterator Bits
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -421,6 +421,12 @@ class PagedDialog(QDialog):
|
|||||||
self._buttonBox.addWidget(buttonBar)
|
self._buttonBox.addWidget(buttonBar)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def setCurrentWidget(self, widget):
|
||||||
|
"""Forward the changing of tab to the QTabWidget.
|
||||||
|
"""
|
||||||
|
self._tabBox.setCurrentWidget(widget)
|
||||||
|
return
|
||||||
|
|
||||||
# END Class PagedDialog
|
# END Class PagedDialog
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -103,14 +103,19 @@ class GuiDocSplit(QDialog):
|
|||||||
self.hierarchySwitch = QSwitch(width=2*iPx, height=iPx)
|
self.hierarchySwitch = QSwitch(width=2*iPx, height=iPx)
|
||||||
self.hierarchySwitch.setChecked(docHierarchy)
|
self.hierarchySwitch.setChecked(docHierarchy)
|
||||||
|
|
||||||
|
self.trashLabel = QLabel(self.tr("Move split document to Trash"))
|
||||||
|
self.trashSwitch = QSwitch(width=2*iPx, height=iPx)
|
||||||
|
|
||||||
self.optBox = QGridLayout()
|
self.optBox = QGridLayout()
|
||||||
self.optBox.addWidget(self.folderLabel, 0, 0)
|
self.optBox.addWidget(self.folderLabel, 0, 0)
|
||||||
self.optBox.addWidget(self.folderSwitch, 0, 1)
|
self.optBox.addWidget(self.folderSwitch, 0, 1)
|
||||||
self.optBox.addWidget(self.hierarchyLabel, 1, 0)
|
self.optBox.addWidget(self.hierarchyLabel, 1, 0)
|
||||||
self.optBox.addWidget(self.hierarchySwitch, 1, 1)
|
self.optBox.addWidget(self.hierarchySwitch, 1, 1)
|
||||||
|
self.optBox.addWidget(self.trashLabel, 2, 0)
|
||||||
|
self.optBox.addWidget(self.trashSwitch, 2, 1)
|
||||||
self.optBox.setVerticalSpacing(vSp)
|
self.optBox.setVerticalSpacing(vSp)
|
||||||
self.optBox.setHorizontalSpacing(hSp)
|
self.optBox.setHorizontalSpacing(hSp)
|
||||||
self.optBox.setColumnStretch(2, 1)
|
self.optBox.setColumnStretch(3, 1)
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
@@ -155,11 +160,13 @@ class GuiDocSplit(QDialog):
|
|||||||
spLevel = self.splitLevel.currentData()
|
spLevel = self.splitLevel.currentData()
|
||||||
intoFolder = self.folderSwitch.isChecked()
|
intoFolder = self.folderSwitch.isChecked()
|
||||||
docHierarchy = self.hierarchySwitch.isChecked()
|
docHierarchy = self.hierarchySwitch.isChecked()
|
||||||
|
moveToTrash = self.trashSwitch.isChecked()
|
||||||
|
|
||||||
self._data["spLevel"] = spLevel
|
self._data["spLevel"] = spLevel
|
||||||
self._data["headerList"] = headerList
|
self._data["headerList"] = headerList
|
||||||
self._data["intoFolder"] = intoFolder
|
self._data["intoFolder"] = intoFolder
|
||||||
self._data["docHierarchy"] = docHierarchy
|
self._data["docHierarchy"] = docHierarchy
|
||||||
|
self._data["moveToTrash"] = moveToTrash
|
||||||
|
|
||||||
pOptions = self.theProject.options
|
pOptions = self.theProject.options
|
||||||
pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
|
pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ from PyQt5.QtWidgets import (
|
|||||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.enum import nwAlert
|
|
||||||
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
|
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
|
||||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||||
|
|
||||||
@@ -78,10 +77,36 @@ class GuiPreferences(PagedDialog):
|
|||||||
|
|
||||||
self.resize(*self.mainConf.getPreferencesSize())
|
self.resize(*self.mainConf.getPreferencesSize())
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
self._updateTheme = False
|
||||||
|
self._updateSyntax = False
|
||||||
|
self._needsRestart = False
|
||||||
|
self._refreshTree = False
|
||||||
|
|
||||||
logger.debug("GuiPreferences initialisation complete")
|
logger.debug("GuiPreferences initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Properties
|
||||||
|
##
|
||||||
|
|
||||||
|
@property
|
||||||
|
def updateTheme(self):
|
||||||
|
return self._updateTheme
|
||||||
|
|
||||||
|
@property
|
||||||
|
def updateSyntax(self):
|
||||||
|
return self._updateSyntax
|
||||||
|
|
||||||
|
@property
|
||||||
|
def needsRestart(self):
|
||||||
|
return self._needsRestart
|
||||||
|
|
||||||
|
@property
|
||||||
|
def refreshTree(self):
|
||||||
|
return self._refreshTree
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
@@ -92,8 +117,7 @@ class GuiPreferences(PagedDialog):
|
|||||||
"""
|
"""
|
||||||
logger.debug("Saving new preferences")
|
logger.debug("Saving new preferences")
|
||||||
|
|
||||||
needsRestart, refreshTree = self.tabGeneral.saveValues()
|
self.tabGeneral.saveValues()
|
||||||
|
|
||||||
self.tabProjects.saveValues()
|
self.tabProjects.saveValues()
|
||||||
self.tabDocs.saveValues()
|
self.tabDocs.saveValues()
|
||||||
self.tabEditor.saveValues()
|
self.tabEditor.saveValues()
|
||||||
@@ -101,14 +125,6 @@ class GuiPreferences(PagedDialog):
|
|||||||
self.tabAuto.saveValues()
|
self.tabAuto.saveValues()
|
||||||
self.tabQuote.saveValues()
|
self.tabQuote.saveValues()
|
||||||
|
|
||||||
if needsRestart:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Some changes will not be applied until novelWriter has been restarted."
|
|
||||||
), nwAlert.INFO)
|
|
||||||
|
|
||||||
if refreshTree:
|
|
||||||
self.mainGui.projView.populateTree()
|
|
||||||
|
|
||||||
self._saveWindowSize()
|
self._saveWindowSize()
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
@@ -140,6 +156,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
super().__init__(parent=prefsGui)
|
super().__init__(parent=prefsGui)
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
|
self.prefsGui = prefsGui
|
||||||
self.mainGui = prefsGui.mainGui
|
self.mainGui = prefsGui.mainGui
|
||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
@@ -166,7 +183,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Main GUI language"),
|
self.tr("Main GUI language"),
|
||||||
self.guiLang,
|
self.guiLang,
|
||||||
self.tr("Requires restart.")
|
self.tr("Requires restart to take effect.")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Select Theme
|
# Select Theme
|
||||||
@@ -182,23 +199,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Main GUI theme"),
|
self.tr("Main GUI theme"),
|
||||||
self.guiTheme,
|
self.guiTheme,
|
||||||
self.tr("Requires restart.")
|
self.tr("General colour theme and icons.")
|
||||||
)
|
|
||||||
|
|
||||||
# Select Icon Theme
|
|
||||||
self.guiIcons = QComboBox()
|
|
||||||
self.guiIcons.setMinimumWidth(minWidth)
|
|
||||||
self.iconCache = self.mainTheme.iconCache.listThemes()
|
|
||||||
for iconDir, iconName in self.iconCache:
|
|
||||||
self.guiIcons.addItem(iconName, iconDir)
|
|
||||||
iconIdx = self.guiIcons.findData(self.mainConf.guiIcons)
|
|
||||||
if iconIdx != -1:
|
|
||||||
self.guiIcons.setCurrentIndex(iconIdx)
|
|
||||||
|
|
||||||
self.mainForm.addRow(
|
|
||||||
self.tr("Main icon theme"),
|
|
||||||
self.guiIcons,
|
|
||||||
self.tr("Requires restart.")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Editor Theme
|
# Editor Theme
|
||||||
@@ -228,7 +229,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Font family"),
|
self.tr("Font family"),
|
||||||
self.guiFont,
|
self.guiFont,
|
||||||
self.tr("Requires restart."),
|
self.tr("Requires restart to take effect."),
|
||||||
theButton=self.fontButton
|
theButton=self.fontButton
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -241,7 +242,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Font size"),
|
self.tr("Font size"),
|
||||||
self.guiFontSize,
|
self.guiFontSize,
|
||||||
self.tr("Requires restart."),
|
self.tr("Requires restart to take effect."),
|
||||||
theUnit=self.tr("pt")
|
theUnit=self.tr("pt")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -288,27 +289,21 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
"""
|
"""
|
||||||
guiLang = self.guiLang.currentData()
|
guiLang = self.guiLang.currentData()
|
||||||
guiTheme = self.guiTheme.currentData()
|
guiTheme = self.guiTheme.currentData()
|
||||||
guiIcons = self.guiIcons.currentData()
|
|
||||||
guiSyntax = self.guiSyntax.currentData()
|
guiSyntax = self.guiSyntax.currentData()
|
||||||
guiFont = self.guiFont.text()
|
guiFont = self.guiFont.text()
|
||||||
guiFontSize = self.guiFontSize.value()
|
guiFontSize = self.guiFontSize.value()
|
||||||
emphLabels = self.emphLabels.isChecked()
|
emphLabels = self.emphLabels.isChecked()
|
||||||
|
|
||||||
# Check if restart is needed
|
# Update Flags
|
||||||
needsRestart = False
|
self.prefsGui._updateTheme |= self.mainConf.guiTheme != guiTheme
|
||||||
needsRestart |= self.mainConf.guiLang != guiLang
|
self.prefsGui._updateSyntax |= self.mainConf.guiSyntax != guiSyntax
|
||||||
needsRestart |= self.mainConf.guiTheme != guiTheme
|
self.prefsGui._needsRestart |= self.mainConf.guiLang != guiLang
|
||||||
needsRestart |= self.mainConf.guiIcons != guiIcons
|
self.prefsGui._needsRestart |= self.mainConf.guiFont != guiFont
|
||||||
needsRestart |= self.mainConf.guiFont != guiFont
|
self.prefsGui._needsRestart |= self.mainConf.guiFontSize != guiFontSize
|
||||||
needsRestart |= self.mainConf.guiFontSize != guiFontSize
|
self.prefsGui._refreshTree |= self.mainConf.emphLabels != emphLabels
|
||||||
|
|
||||||
# Check if refreshing project tree is needed
|
|
||||||
refreshTree = False
|
|
||||||
refreshTree |= self.mainConf.emphLabels != emphLabels
|
|
||||||
|
|
||||||
self.mainConf.guiLang = guiLang
|
self.mainConf.guiLang = guiLang
|
||||||
self.mainConf.guiTheme = guiTheme
|
self.mainConf.guiTheme = guiTheme
|
||||||
self.mainConf.guiIcons = guiIcons
|
|
||||||
self.mainConf.guiSyntax = guiSyntax
|
self.mainConf.guiSyntax = guiSyntax
|
||||||
self.mainConf.guiFont = guiFont
|
self.mainConf.guiFont = guiFont
|
||||||
self.mainConf.guiFontSize = guiFontSize
|
self.mainConf.guiFontSize = guiFontSize
|
||||||
@@ -319,7 +314,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
|
|
||||||
self.mainConf.confChanged = True
|
self.mainConf.confChanged = True
|
||||||
|
|
||||||
return needsRestart, refreshTree
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
|
|||||||
@@ -43,7 +43,12 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class GuiProjectSettings(PagedDialog):
|
class GuiProjectSettings(PagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
TAB_MAIN = 0
|
||||||
|
TAB_STATUS = 1
|
||||||
|
TAB_IMPORT = 2
|
||||||
|
TAB_REPLACE = 3
|
||||||
|
|
||||||
|
def __init__(self, mainGui, focusTab=TAB_MAIN):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectSettings ...")
|
logger.debug("Initialising GuiProjectSettings ...")
|
||||||
@@ -83,12 +88,19 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
self.addControls(self.buttonBox)
|
self.addControls(self.buttonBox)
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
self.spellChanged = False
|
self._spellChanged = False
|
||||||
|
|
||||||
|
# Focus Tab
|
||||||
|
self._focusTab(focusTab)
|
||||||
|
|
||||||
logger.debug("GuiProjectSettings initialisation complete")
|
logger.debug("GuiProjectSettings initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@property
|
||||||
|
def spellChanged(self):
|
||||||
|
return self._spellChanged
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
@@ -108,7 +120,7 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
self.theProject.setProjBackup(doBackup)
|
self.theProject.setProjBackup(doBackup)
|
||||||
|
|
||||||
# Remember this as updating spell dictionary can be expensive
|
# Remember this as updating spell dictionary can be expensive
|
||||||
self.spellChanged = self.theProject.setSpellLang(spellLang)
|
self._spellChanged = self.theProject.setSpellLang(spellLang)
|
||||||
|
|
||||||
if self.tabStatus.colChanged:
|
if self.tabStatus.colChanged:
|
||||||
newList, delList = self.tabStatus.getNewList()
|
newList, delList = self.tabStatus.getNewList()
|
||||||
@@ -141,6 +153,19 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def _focusTab(self, tab):
|
||||||
|
"""Change which is the focused tab.
|
||||||
|
"""
|
||||||
|
if tab == self.TAB_MAIN:
|
||||||
|
self.setCurrentWidget(self.tabMain)
|
||||||
|
elif tab == self.TAB_STATUS:
|
||||||
|
self.setCurrentWidget(self.tabStatus)
|
||||||
|
elif tab == self.TAB_IMPORT:
|
||||||
|
self.setCurrentWidget(self.tabImport)
|
||||||
|
elif tab == self.TAB_REPLACE:
|
||||||
|
self.setCurrentWidget(self.tabReplace)
|
||||||
|
return
|
||||||
|
|
||||||
def _saveGuiSettings(self):
|
def _saveGuiSettings(self):
|
||||||
"""Save GUI settings.
|
"""Save GUI settings.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -112,9 +112,10 @@ class nwDocInsert(Enum):
|
|||||||
QUOTE_RS = 2
|
QUOTE_RS = 2
|
||||||
QUOTE_LD = 3
|
QUOTE_LD = 3
|
||||||
QUOTE_RD = 4
|
QUOTE_RD = 4
|
||||||
NEW_PAGE = 5
|
SYNOPSIS = 5
|
||||||
VSPACE_S = 6
|
NEW_PAGE = 6
|
||||||
VSPACE_M = 7
|
VSPACE_S = 7
|
||||||
|
VSPACE_M = 8
|
||||||
|
|
||||||
# END Enum nwDocInsert
|
# END Enum nwDocInsert
|
||||||
|
|
||||||
|
|||||||
@@ -138,24 +138,20 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.setFrameStyle(QFrame.NoFrame)
|
self.setFrameStyle(QFrame.NoFrame)
|
||||||
|
|
||||||
# Custom Shortcuts
|
# Custom Shortcuts
|
||||||
QShortcut(
|
self.keyContext = QShortcut(self)
|
||||||
QKeySequence("Ctrl+."),
|
self.keyContext.setKey("Ctrl+.")
|
||||||
self,
|
self.keyContext.setContext(Qt.WidgetShortcut)
|
||||||
context=Qt.WidgetShortcut,
|
self.keyContext.activated.connect(self._openSpellContext)
|
||||||
activated=self._openSpellContext
|
|
||||||
)
|
self.followTag1 = QShortcut(self)
|
||||||
QShortcut(
|
self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier)
|
||||||
Qt.Key_Return | Qt.ControlModifier,
|
self.followTag1.setContext(Qt.WidgetShortcut)
|
||||||
self,
|
self.followTag1.activated.connect(self._followTag)
|
||||||
context=Qt.WidgetShortcut,
|
|
||||||
activated=self._followTag
|
self.followTag2 = QShortcut(self)
|
||||||
)
|
self.followTag2.setKey(Qt.Key_Enter | Qt.ControlModifier)
|
||||||
QShortcut(
|
self.followTag2.setContext(Qt.WidgetShortcut)
|
||||||
Qt.Key_Enter | Qt.ControlModifier,
|
self.followTag2.activated.connect(self._followTag)
|
||||||
self,
|
|
||||||
context=Qt.WidgetShortcut,
|
|
||||||
activated=self._followTag
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set Up Document Word Counter
|
# Set Up Document Word Counter
|
||||||
self.wcTimerDoc = QTimer()
|
self.wcTimerDoc = QTimer()
|
||||||
@@ -177,6 +173,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.wCounterSel.signals.countsReady.connect(self._updateSelCounts)
|
self.wCounterSel.signals.countsReady.connect(self._updateSelCounts)
|
||||||
|
|
||||||
# Finalise
|
# Finalise
|
||||||
|
self.updateSyntaxColours()
|
||||||
self.initEditor()
|
self.initEditor()
|
||||||
|
|
||||||
logger.debug("GuiDocEditor initialisation complete")
|
logger.debug("GuiDocEditor initialisation complete")
|
||||||
@@ -210,6 +207,35 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements
|
||||||
|
"""
|
||||||
|
self.docSearch.updateTheme()
|
||||||
|
self.docHeader.updateTheme()
|
||||||
|
self.docFooter.updateTheme()
|
||||||
|
return
|
||||||
|
|
||||||
|
def updateSyntaxColours(self):
|
||||||
|
"""Update the syntax highlighting theme.
|
||||||
|
"""
|
||||||
|
mainPalette = self.palette()
|
||||||
|
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
||||||
|
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
||||||
|
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||||
|
self.setPalette(mainPalette)
|
||||||
|
|
||||||
|
docPalette = self.viewport().palette()
|
||||||
|
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
||||||
|
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
||||||
|
self.viewport().setPalette(docPalette)
|
||||||
|
|
||||||
|
self.docHeader.matchColours()
|
||||||
|
self.docFooter.matchColours()
|
||||||
|
|
||||||
|
self.highLight.initHighlighter()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def initEditor(self):
|
def initEditor(self):
|
||||||
"""Initialise or re-initialise the editor with the user's
|
"""Initialise or re-initialise the editor with the user's
|
||||||
settings. This function is both called when the editor is
|
settings. This function is both called when the editor is
|
||||||
@@ -256,21 +282,6 @@ class GuiDocEditor(QTextEdit):
|
|||||||
theFont.setPointSize(self.mainConf.textSize)
|
theFont.setPointSize(self.mainConf.textSize)
|
||||||
self.setFont(theFont)
|
self.setFont(theFont)
|
||||||
|
|
||||||
# Set the widget colours to match syntax theme
|
|
||||||
mainPalette = self.palette()
|
|
||||||
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
|
|
||||||
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
|
||||||
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
|
||||||
self.setPalette(mainPalette)
|
|
||||||
|
|
||||||
docPalette = self.viewport().palette()
|
|
||||||
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
|
|
||||||
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
|
|
||||||
self.viewport().setPalette(docPalette)
|
|
||||||
|
|
||||||
self.docHeader.matchColours()
|
|
||||||
self.docFooter.matchColours()
|
|
||||||
|
|
||||||
# Set default text margins
|
# Set default text margins
|
||||||
# Due to cursor visibility, a part of the margin must be
|
# Due to cursor visibility, a part of the margin must be
|
||||||
# allocated to the document itself. See issue #1112.
|
# allocated to the document itself. See issue #1112.
|
||||||
@@ -305,9 +316,6 @@ class GuiDocEditor(QTextEdit):
|
|||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
|
|
||||||
# Initialise the syntax highlighter
|
|
||||||
self.highLight.initHighlighter()
|
|
||||||
|
|
||||||
# Configure word count timer
|
# Configure word count timer
|
||||||
self.wcInterval = self.mainConf.wordCountTimer
|
self.wcInterval = self.mainConf.wordCountTimer
|
||||||
self.wcTimerDoc.setInterval(int(self.wcInterval*1000))
|
self.wcTimerDoc.setInterval(int(self.wcInterval*1000))
|
||||||
@@ -869,6 +877,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
newBlock = False
|
newBlock = False
|
||||||
|
goAfter = False
|
||||||
|
|
||||||
if isinstance(theInsert, str):
|
if isinstance(theInsert, str):
|
||||||
theText = theInsert
|
theText = theInsert
|
||||||
@@ -881,22 +890,29 @@ class GuiDocEditor(QTextEdit):
|
|||||||
theText = self._typDQOpen
|
theText = self._typDQOpen
|
||||||
elif theInsert == nwDocInsert.QUOTE_RD:
|
elif theInsert == nwDocInsert.QUOTE_RD:
|
||||||
theText = self._typDQClose
|
theText = self._typDQClose
|
||||||
|
elif theInsert == nwDocInsert.SYNOPSIS:
|
||||||
|
theText = "% Synopsis: "
|
||||||
|
newBlock = True
|
||||||
|
goAfter = True
|
||||||
elif theInsert == nwDocInsert.NEW_PAGE:
|
elif theInsert == nwDocInsert.NEW_PAGE:
|
||||||
theText = "[NEW PAGE]"
|
theText = "[NEW PAGE]"
|
||||||
newBlock = True
|
newBlock = True
|
||||||
|
goAfter = False
|
||||||
elif theInsert == nwDocInsert.VSPACE_S:
|
elif theInsert == nwDocInsert.VSPACE_S:
|
||||||
theText = "[VSPACE]"
|
theText = "[VSPACE]"
|
||||||
newBlock = True
|
newBlock = True
|
||||||
|
goAfter = False
|
||||||
elif theInsert == nwDocInsert.VSPACE_M:
|
elif theInsert == nwDocInsert.VSPACE_M:
|
||||||
theText = "[VSPACE:2]"
|
theText = "[VSPACE:2]"
|
||||||
newBlock = True
|
newBlock = True
|
||||||
|
goAfter = False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if newBlock:
|
if newBlock:
|
||||||
self.insertNewBlock(theText, defaultAfter=False)
|
self.insertNewBlock(theText, defaultAfter=goAfter)
|
||||||
else:
|
else:
|
||||||
theCursor = self.textCursor()
|
theCursor = self.textCursor()
|
||||||
theCursor.beginEditBlock()
|
theCursor.beginEditBlock()
|
||||||
@@ -1162,6 +1178,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
posCursor = self.cursorForPosition(thePos)
|
posCursor = self.cursorForPosition(thePos)
|
||||||
spellCheck = self._spellCheck
|
spellCheck = self._spellCheck
|
||||||
|
theWord = ""
|
||||||
|
|
||||||
if posCursor.block().text().startswith("@"):
|
if posCursor.block().text().startswith("@"):
|
||||||
spellCheck = False
|
spellCheck = False
|
||||||
@@ -1430,6 +1447,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
origB = theCursor.selectionEnd()
|
origB = theCursor.selectionEnd()
|
||||||
else:
|
else:
|
||||||
origA = theCursor.position()
|
origA = theCursor.position()
|
||||||
|
origB = theCursor.position()
|
||||||
|
|
||||||
findOpt = QTextDocument.FindFlag(0)
|
findOpt = QTextDocument.FindFlag(0)
|
||||||
if self.docSearch.isCaseSense:
|
if self.docSearch.isCaseSense:
|
||||||
@@ -1699,12 +1717,8 @@ class GuiDocEditor(QTextEdit):
|
|||||||
logger.debug("Invalid block selected for action '%s'", str(docAction))
|
logger.debug("Invalid block selected for action '%s'", str(docAction))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
theText = theBlock.text()
|
|
||||||
if len(theText.strip()) == 0:
|
|
||||||
logger.debug("Empty block selected for action '%s'", str(docAction))
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Remove existing format first, if any
|
# Remove existing format first, if any
|
||||||
|
theText = theBlock.text()
|
||||||
if theText.startswith("@"):
|
if theText.startswith("@"):
|
||||||
logger.error("Cannot apply block format to keyword/value line")
|
logger.error("Cannot apply block format to keyword/value line")
|
||||||
return False
|
return False
|
||||||
@@ -1874,10 +1888,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
def _followTag(self, theCursor=None, loadTag=True):
|
def _followTag(self, theCursor=None, loadTag=True):
|
||||||
"""Activated by Ctrl+Enter. Checks that we're in a block
|
"""Activated by Ctrl+Enter. Checks that we're in a block
|
||||||
starting with '@'. We then find the word under the cursor and
|
starting with '@'. We then find the tag under the cursor and
|
||||||
check that it is after the ':'. If all this is fine, we have a
|
check that it is not the tag itself. If all this is fine, we
|
||||||
tag and can tell the document viewer to try and find and load
|
have a tag and can tell the document viewer to try and find and
|
||||||
the file where the tag is defined.
|
load the file where the tag is defined.
|
||||||
"""
|
"""
|
||||||
if theCursor is None:
|
if theCursor is None:
|
||||||
theCursor = self.textCursor()
|
theCursor = self.textCursor()
|
||||||
@@ -1890,18 +1904,29 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
if theText.startswith("@"):
|
if theText.startswith("@"):
|
||||||
|
|
||||||
theCursor.select(QTextCursor.WordUnderCursor)
|
isGood, tBits, tPos = self.theProject.index.scanThis(theText)
|
||||||
theWord = theCursor.selectedText()
|
if not isGood:
|
||||||
cPos = theText.find(":")
|
return False
|
||||||
wPos = theCursor.selectionStart() - theBlock.position()
|
|
||||||
if wPos <= cPos:
|
theTag = ""
|
||||||
|
cPos = theCursor.selectionStart() - theBlock.position()
|
||||||
|
for sTag, sPos in zip(reversed(tBits), reversed(tPos)):
|
||||||
|
if cPos >= sPos:
|
||||||
|
# The cursor is between the start of two tags
|
||||||
|
if cPos <= sPos + len(sTag):
|
||||||
|
# The cursor is inside or at the edge of the tag
|
||||||
|
theTag = sTag
|
||||||
|
break
|
||||||
|
|
||||||
|
if not theTag or theTag.startswith("@"):
|
||||||
|
# The keyword cannot be looked up, so we ignore that
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if loadTag:
|
if loadTag:
|
||||||
logger.debug("Attempting to follow tag '%s'", theWord)
|
logger.debug("Attempting to follow tag '%s'", theTag)
|
||||||
self.loadDocumentTagRequest.emit(theWord, nwDocMode.VIEW)
|
self.loadDocumentTagRequest.emit(theTag, nwDocMode.VIEW)
|
||||||
else:
|
else:
|
||||||
logger.debug("Potential tag '%s'", theWord)
|
logger.debug("Potential tag '%s'", theTag)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -2242,7 +2267,6 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.searchOpt.setIconSize(QSize(tPx, tPx))
|
self.searchOpt.setIconSize(QSize(tPx, tPx))
|
||||||
self.searchOpt.setContentsMargins(0, 0, 0, 0)
|
self.searchOpt.setContentsMargins(0, 0, 0, 0)
|
||||||
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
|
|
||||||
|
|
||||||
self.searchLabel = QLabel(self.tr("Search"))
|
self.searchLabel = QLabel(self.tr("Search"))
|
||||||
self.searchLabel.setFont(self.boxFont)
|
self.searchLabel.setFont(self.boxFont)
|
||||||
@@ -2253,35 +2277,30 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont))
|
self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont))
|
||||||
|
|
||||||
self.toggleCase = QAction(self.tr("Case Sensitive"), self)
|
self.toggleCase = QAction(self.tr("Case Sensitive"), self)
|
||||||
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
|
|
||||||
self.toggleCase.setCheckable(True)
|
self.toggleCase.setCheckable(True)
|
||||||
self.toggleCase.setChecked(self.isCaseSense)
|
self.toggleCase.setChecked(self.isCaseSense)
|
||||||
self.toggleCase.toggled.connect(self._doToggleCase)
|
self.toggleCase.toggled.connect(self._doToggleCase)
|
||||||
self.searchOpt.addAction(self.toggleCase)
|
self.searchOpt.addAction(self.toggleCase)
|
||||||
|
|
||||||
self.toggleWord = QAction(self.tr("Whole Words Only"), self)
|
self.toggleWord = QAction(self.tr("Whole Words Only"), self)
|
||||||
self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
|
|
||||||
self.toggleWord.setCheckable(True)
|
self.toggleWord.setCheckable(True)
|
||||||
self.toggleWord.setChecked(self.isWholeWord)
|
self.toggleWord.setChecked(self.isWholeWord)
|
||||||
self.toggleWord.toggled.connect(self._doToggleWord)
|
self.toggleWord.toggled.connect(self._doToggleWord)
|
||||||
self.searchOpt.addAction(self.toggleWord)
|
self.searchOpt.addAction(self.toggleWord)
|
||||||
|
|
||||||
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
|
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
|
||||||
self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
|
|
||||||
self.toggleRegEx.setCheckable(True)
|
self.toggleRegEx.setCheckable(True)
|
||||||
self.toggleRegEx.setChecked(self.isRegEx)
|
self.toggleRegEx.setChecked(self.isRegEx)
|
||||||
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
|
self.toggleRegEx.toggled.connect(self._doToggleRegEx)
|
||||||
self.searchOpt.addAction(self.toggleRegEx)
|
self.searchOpt.addAction(self.toggleRegEx)
|
||||||
|
|
||||||
self.toggleLoop = QAction(self.tr("Loop Search"), self)
|
self.toggleLoop = QAction(self.tr("Loop Search"), self)
|
||||||
self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
|
|
||||||
self.toggleLoop.setCheckable(True)
|
self.toggleLoop.setCheckable(True)
|
||||||
self.toggleLoop.setChecked(self.doLoop)
|
self.toggleLoop.setChecked(self.doLoop)
|
||||||
self.toggleLoop.toggled.connect(self._doToggleLoop)
|
self.toggleLoop.toggled.connect(self._doToggleLoop)
|
||||||
self.searchOpt.addAction(self.toggleLoop)
|
self.searchOpt.addAction(self.toggleLoop)
|
||||||
|
|
||||||
self.toggleProject = QAction(self.tr("Search Next File"), self)
|
self.toggleProject = QAction(self.tr("Search Next File"), self)
|
||||||
self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
|
|
||||||
self.toggleProject.setCheckable(True)
|
self.toggleProject.setCheckable(True)
|
||||||
self.toggleProject.setChecked(self.doNextFile)
|
self.toggleProject.setChecked(self.doNextFile)
|
||||||
self.toggleProject.toggled.connect(self._doToggleProject)
|
self.toggleProject.toggled.connect(self._doToggleProject)
|
||||||
@@ -2290,7 +2309,6 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.searchOpt.addSeparator()
|
self.searchOpt.addSeparator()
|
||||||
|
|
||||||
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
|
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
|
||||||
self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
|
|
||||||
self.toggleMatchCap.setCheckable(True)
|
self.toggleMatchCap.setCheckable(True)
|
||||||
self.toggleMatchCap.setChecked(self.doMatchCap)
|
self.toggleMatchCap.setChecked(self.doMatchCap)
|
||||||
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
|
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
|
||||||
@@ -2299,7 +2317,6 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.searchOpt.addSeparator()
|
self.searchOpt.addSeparator()
|
||||||
|
|
||||||
self.cancelSearch = QAction(self.tr("Close Search"), self)
|
self.cancelSearch = QAction(self.tr("Close Search"), self)
|
||||||
self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
|
|
||||||
self.cancelSearch.triggered.connect(self._doClose)
|
self.cancelSearch.triggered.connect(self._doClose)
|
||||||
self.searchOpt.addAction(self.cancelSearch)
|
self.searchOpt.addAction(self.cancelSearch)
|
||||||
|
|
||||||
@@ -2311,15 +2328,14 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.showReplace = QToolButton(self)
|
self.showReplace = QToolButton(self)
|
||||||
self.showReplace.setArrowType(Qt.RightArrow)
|
self.showReplace.setArrowType(Qt.RightArrow)
|
||||||
self.showReplace.setCheckable(True)
|
self.showReplace.setCheckable(True)
|
||||||
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
|
|
||||||
self.showReplace.toggled.connect(self._doToggleReplace)
|
self.showReplace.toggled.connect(self._doToggleReplace)
|
||||||
|
|
||||||
self.searchButton = QPushButton(self.mainTheme.getIcon("search"), "")
|
self.searchButton = QPushButton("")
|
||||||
self.searchButton.setFixedSize(QSize(bPx, bPx))
|
self.searchButton.setFixedSize(QSize(bPx, bPx))
|
||||||
self.searchButton.setToolTip(self.tr("Find in current document"))
|
self.searchButton.setToolTip(self.tr("Find in current document"))
|
||||||
self.searchButton.clicked.connect(self._doSearch)
|
self.searchButton.clicked.connect(self._doSearch)
|
||||||
|
|
||||||
self.replaceButton = QPushButton(self.mainTheme.getIcon("search_replace"), "")
|
self.replaceButton = QPushButton("")
|
||||||
self.replaceButton.setFixedSize(QSize(bPx, bPx))
|
self.replaceButton.setFixedSize(QSize(bPx, bPx))
|
||||||
self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
|
self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
|
||||||
self.replaceButton.clicked.connect(self._doReplace)
|
self.replaceButton.clicked.connect(self._doReplace)
|
||||||
@@ -2349,6 +2365,35 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.replaceButton.setVisible(False)
|
self.replaceButton.setVisible(False)
|
||||||
self.adjustSize()
|
self.adjustSize()
|
||||||
|
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
|
logger.debug("GuiDocEditSearch initialisation complete")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
qPalette = qApp.palette()
|
||||||
|
self.setPalette(qPalette)
|
||||||
|
self.searchBox.setPalette(qPalette)
|
||||||
|
self.replaceBox.setPalette(qPalette)
|
||||||
|
|
||||||
|
# Set icons
|
||||||
|
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
|
||||||
|
self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
|
||||||
|
self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
|
||||||
|
self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
|
||||||
|
self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
|
||||||
|
self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
|
||||||
|
self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
|
||||||
|
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
|
||||||
|
self.replaceButton.setIcon(self.mainTheme.getIcon("search_replace"))
|
||||||
|
|
||||||
|
# Set stylesheets
|
||||||
|
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
|
||||||
|
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
|
||||||
|
|
||||||
# Construct Box Colours
|
# Construct Box Colours
|
||||||
qPalette = self.searchBox.palette()
|
qPalette = self.searchBox.palette()
|
||||||
baseCol = qPalette.base().color()
|
baseCol = qPalette.base().color()
|
||||||
@@ -2367,8 +2412,6 @@ class GuiDocEditSearch(QFrame):
|
|||||||
False: errCol
|
False: errCol
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("GuiDocEditSearch initialisation complete")
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def closeSearch(self):
|
def closeSearch(self):
|
||||||
@@ -2394,10 +2437,10 @@ class GuiDocEditSearch(QFrame):
|
|||||||
"""
|
"""
|
||||||
if self.replaceBox.isVisible():
|
if self.replaceBox.isVisible():
|
||||||
if self.searchBox.hasFocus():
|
if self.searchBox.hasFocus():
|
||||||
self.replaceBox.setFocus(True)
|
self.replaceBox.setFocus()
|
||||||
return True
|
return True
|
||||||
elif self.replaceBox.hasFocus():
|
elif self.replaceBox.hasFocus():
|
||||||
self.searchBox.setFocus(True)
|
self.searchBox.setFocus()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -2487,12 +2530,14 @@ class GuiDocEditSearch(QFrame):
|
|||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _doClose(self):
|
def _doClose(self):
|
||||||
"""Hide the search/replace bar.
|
"""Hide the search/replace bar.
|
||||||
"""
|
"""
|
||||||
self.closeSearch()
|
self.closeSearch()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _doSearch(self):
|
def _doSearch(self):
|
||||||
"""Call the search action function for the document editor.
|
"""Call the search action function for the document editor.
|
||||||
"""
|
"""
|
||||||
@@ -2503,12 +2548,14 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.docEditor.findNext()
|
self.docEditor.findNext()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _doReplace(self):
|
def _doReplace(self):
|
||||||
"""Call the replace action function for the document editor.
|
"""Call the replace action function for the document editor.
|
||||||
"""
|
"""
|
||||||
self.docEditor.replaceNext()
|
self.docEditor.replaceNext()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleReplace(self, theState):
|
def _doToggleReplace(self, theState):
|
||||||
"""Toggle the show/hide of the replace box.
|
"""Toggle the show/hide of the replace box.
|
||||||
"""
|
"""
|
||||||
@@ -2523,36 +2570,42 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.docEditor.updateDocMargins()
|
self.docEditor.updateDocMargins()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleCase(self, theState):
|
def _doToggleCase(self, theState):
|
||||||
"""Enable/disable case sensitive mode.
|
"""Enable/disable case sensitive mode.
|
||||||
"""
|
"""
|
||||||
self.isCaseSense = theState
|
self.isCaseSense = theState
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleWord(self, theState):
|
def _doToggleWord(self, theState):
|
||||||
"""Enable/disable whole word search mode.
|
"""Enable/disable whole word search mode.
|
||||||
"""
|
"""
|
||||||
self.isWholeWord = theState
|
self.isWholeWord = theState
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleRegEx(self, theState):
|
def _doToggleRegEx(self, theState):
|
||||||
"""Enable/disable regular expression search mode.
|
"""Enable/disable regular expression search mode.
|
||||||
"""
|
"""
|
||||||
self.isRegEx = theState
|
self.isRegEx = theState
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleLoop(self, theState):
|
def _doToggleLoop(self, theState):
|
||||||
"""Enable/disable looping the search.
|
"""Enable/disable looping the search.
|
||||||
"""
|
"""
|
||||||
self.doLoop = theState
|
self.doLoop = theState
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleProject(self, theState):
|
def _doToggleProject(self, theState):
|
||||||
"""Enable/disable continuing search in next project file.
|
"""Enable/disable continuing search in next project file.
|
||||||
"""
|
"""
|
||||||
self.doNextFile = theState
|
self.doNextFile = theState
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleMatchCap(self, theState):
|
def _doToggleMatchCap(self, theState):
|
||||||
"""Enable/disable preserving capitalisation when replacing.
|
"""Enable/disable preserving capitalisation when replacing.
|
||||||
"""
|
"""
|
||||||
@@ -2615,51 +2668,38 @@ class GuiDocEditHeader(QWidget):
|
|||||||
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||||
self.theTitle.setFont(lblFont)
|
self.theTitle.setFont(lblFont)
|
||||||
|
|
||||||
buttonStyle = (
|
|
||||||
"QToolButton {{border: none; background: transparent;}} "
|
|
||||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
|
||||||
).format(*self.mainTheme.colText)
|
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.editButton = QToolButton(self)
|
self.editButton = QToolButton(self)
|
||||||
self.editButton.setIcon(self.mainTheme.getIcon("edit"))
|
|
||||||
self.editButton.setContentsMargins(0, 0, 0, 0)
|
self.editButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.editButton.setIconSize(QSize(fPx, fPx))
|
self.editButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.editButton.setFixedSize(fPx, fPx)
|
self.editButton.setFixedSize(fPx, fPx)
|
||||||
self.editButton.setStyleSheet(buttonStyle)
|
|
||||||
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.editButton.setVisible(False)
|
self.editButton.setVisible(False)
|
||||||
self.editButton.setToolTip(self.tr("Edit document label"))
|
self.editButton.setToolTip(self.tr("Edit document label"))
|
||||||
self.editButton.clicked.connect(self._editDocument)
|
self.editButton.clicked.connect(self._editDocument)
|
||||||
|
|
||||||
self.searchButton = QToolButton(self)
|
self.searchButton = QToolButton(self)
|
||||||
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
|
|
||||||
self.searchButton.setContentsMargins(0, 0, 0, 0)
|
self.searchButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.searchButton.setIconSize(QSize(fPx, fPx))
|
self.searchButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.searchButton.setFixedSize(fPx, fPx)
|
self.searchButton.setFixedSize(fPx, fPx)
|
||||||
self.searchButton.setStyleSheet(buttonStyle)
|
|
||||||
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.searchButton.setVisible(False)
|
self.searchButton.setVisible(False)
|
||||||
self.searchButton.setToolTip(self.tr("Search document"))
|
self.searchButton.setToolTip(self.tr("Search document"))
|
||||||
self.searchButton.clicked.connect(self._searchDocument)
|
self.searchButton.clicked.connect(self._searchDocument)
|
||||||
|
|
||||||
self.minmaxButton = QToolButton(self)
|
self.minmaxButton = QToolButton(self)
|
||||||
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
|
|
||||||
self.minmaxButton.setContentsMargins(0, 0, 0, 0)
|
self.minmaxButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.minmaxButton.setIconSize(QSize(fPx, fPx))
|
self.minmaxButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.minmaxButton.setFixedSize(fPx, fPx)
|
self.minmaxButton.setFixedSize(fPx, fPx)
|
||||||
self.minmaxButton.setStyleSheet(buttonStyle)
|
|
||||||
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.minmaxButton.setVisible(False)
|
self.minmaxButton.setVisible(False)
|
||||||
self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
|
self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
|
||||||
self.minmaxButton.clicked.connect(self._minmaxDocument)
|
self.minmaxButton.clicked.connect(self._minmaxDocument)
|
||||||
|
|
||||||
self.closeButton = QToolButton(self)
|
self.closeButton = QToolButton(self)
|
||||||
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
|
|
||||||
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.closeButton.setIconSize(QSize(fPx, fPx))
|
self.closeButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.closeButton.setFixedSize(fPx, fPx)
|
self.closeButton.setFixedSize(fPx, fPx)
|
||||||
self.closeButton.setStyleSheet(buttonStyle)
|
|
||||||
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.closeButton.setVisible(False)
|
self.closeButton.setVisible(False)
|
||||||
self.closeButton.setToolTip(self.tr("Close the document"))
|
self.closeButton.setToolTip(self.tr("Close the document"))
|
||||||
@@ -2682,8 +2722,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
self.outerBox.setContentsMargins(cM, cM, cM, cM)
|
self.outerBox.setContentsMargins(cM, cM, cM, cM)
|
||||||
self.setMinimumHeight(fPx + 2*cM)
|
self.setMinimumHeight(fPx + 2*cM)
|
||||||
|
|
||||||
# Fix the Colours
|
self.updateTheme()
|
||||||
self.matchColours()
|
|
||||||
|
|
||||||
logger.debug("GuiDocEditHeader initialisation complete")
|
logger.debug("GuiDocEditHeader initialisation complete")
|
||||||
|
|
||||||
@@ -2693,6 +2732,28 @@ class GuiDocEditHeader(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.editButton.setIcon(self.mainTheme.getIcon("edit"))
|
||||||
|
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
|
||||||
|
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
|
||||||
|
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
|
||||||
|
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||||
|
).format(*self.mainTheme.colText)
|
||||||
|
|
||||||
|
self.editButton.setStyleSheet(buttonStyle)
|
||||||
|
self.searchButton.setStyleSheet(buttonStyle)
|
||||||
|
self.minmaxButton.setStyleSheet(buttonStyle)
|
||||||
|
self.closeButton.setStyleSheet(buttonStyle)
|
||||||
|
|
||||||
|
self.matchColours()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def matchColours(self):
|
def matchColours(self):
|
||||||
"""Update the colours of the widget to match those of the syntax
|
"""Update the colours of the widget to match those of the syntax
|
||||||
theme rather than the main GUI.
|
theme rather than the main GUI.
|
||||||
@@ -2757,18 +2818,21 @@ class GuiDocEditHeader(QWidget):
|
|||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _editDocument(self):
|
def _editDocument(self):
|
||||||
"""Open the edit item dialog from the main GUI.
|
"""Open the edit item dialog from the main GUI.
|
||||||
"""
|
"""
|
||||||
self.mainGui.editItemLabel(self._docHandle)
|
self.mainGui.editItemLabel(self._docHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _searchDocument(self):
|
def _searchDocument(self):
|
||||||
"""Toggle the visibility of the search box.
|
"""Toggle the visibility of the search box.
|
||||||
"""
|
"""
|
||||||
self.docEditor.toggleSearch()
|
self.docEditor.toggleSearch()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _closeDocument(self):
|
def _closeDocument(self):
|
||||||
"""Trigger the close editor on the main window.
|
"""Trigger the close editor on the main window.
|
||||||
"""
|
"""
|
||||||
@@ -2779,6 +2843,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
self.minmaxButton.setVisible(False)
|
self.minmaxButton.setVisible(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _minmaxDocument(self):
|
def _minmaxDocument(self):
|
||||||
"""Switch on or off Focus Mode.
|
"""Switch on or off Focus Mode.
|
||||||
"""
|
"""
|
||||||
@@ -2851,7 +2916,6 @@ class GuiDocEditFooter(QWidget):
|
|||||||
|
|
||||||
# Lines
|
# Lines
|
||||||
self.linesIcon = QLabel("")
|
self.linesIcon = QLabel("")
|
||||||
self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
|
|
||||||
self.linesIcon.setContentsMargins(0, 0, 0, 0)
|
self.linesIcon.setContentsMargins(0, 0, 0, 0)
|
||||||
self.linesIcon.setFixedHeight(self.sPx)
|
self.linesIcon.setFixedHeight(self.sPx)
|
||||||
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||||
@@ -2867,7 +2931,6 @@ class GuiDocEditFooter(QWidget):
|
|||||||
|
|
||||||
# Words
|
# Words
|
||||||
self.wordsIcon = QLabel("")
|
self.wordsIcon = QLabel("")
|
||||||
self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
|
|
||||||
self.wordsIcon.setContentsMargins(0, 0, 0, 0)
|
self.wordsIcon.setContentsMargins(0, 0, 0, 0)
|
||||||
self.wordsIcon.setFixedHeight(self.sPx)
|
self.wordsIcon.setFixedHeight(self.sPx)
|
||||||
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||||
@@ -2902,7 +2965,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self.setMinimumHeight(fPx + 2*cM)
|
self.setMinimumHeight(fPx + 2*cM)
|
||||||
|
|
||||||
# Fix the Colours
|
# Fix the Colours
|
||||||
self.matchColours()
|
self.updateTheme()
|
||||||
self.updateLineCount()
|
self.updateLineCount()
|
||||||
self.updateCounts()
|
self.updateCounts()
|
||||||
|
|
||||||
@@ -2914,6 +2977,16 @@ class GuiDocEditFooter(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
|
||||||
|
self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
|
||||||
|
|
||||||
|
self.matchColours()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def matchColours(self):
|
def matchColours(self):
|
||||||
"""Update the colours of the widget to match those of the syntax
|
"""Update the colours of the widget to match those of the syntax
|
||||||
theme rather than the main GUI.
|
theme rather than the main GUI.
|
||||||
|
|||||||
@@ -102,6 +102,13 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
self.docHeader.setTitleFromHandle(self._docHandle)
|
self.docHeader.setTitleFromHandle(self._docHandle)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.docHeader.updateTheme()
|
||||||
|
self.docFooter.updateTheme()
|
||||||
|
return
|
||||||
|
|
||||||
def initViewer(self):
|
def initViewer(self):
|
||||||
"""Set editor settings from main config.
|
"""Set editor settings from main config.
|
||||||
"""
|
"""
|
||||||
@@ -735,51 +742,38 @@ class GuiDocViewHeader(QWidget):
|
|||||||
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||||
self.theTitle.setFont(lblFont)
|
self.theTitle.setFont(lblFont)
|
||||||
|
|
||||||
buttonStyle = (
|
|
||||||
"QToolButton {{border: none; background: transparent;}} "
|
|
||||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
|
||||||
).format(*self.mainTheme.colText)
|
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.backButton = QToolButton(self)
|
self.backButton = QToolButton(self)
|
||||||
self.backButton.setIcon(self.mainTheme.getIcon("backward"))
|
|
||||||
self.backButton.setContentsMargins(0, 0, 0, 0)
|
self.backButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.backButton.setIconSize(QSize(fPx, fPx))
|
self.backButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.backButton.setFixedSize(fPx, fPx)
|
self.backButton.setFixedSize(fPx, fPx)
|
||||||
self.backButton.setStyleSheet(buttonStyle)
|
|
||||||
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.backButton.setVisible(False)
|
self.backButton.setVisible(False)
|
||||||
self.backButton.setToolTip(self.tr("Go backward"))
|
self.backButton.setToolTip(self.tr("Go backward"))
|
||||||
self.backButton.clicked.connect(self.docViewer.navBackward)
|
self.backButton.clicked.connect(self.docViewer.navBackward)
|
||||||
|
|
||||||
self.forwardButton = QToolButton(self)
|
self.forwardButton = QToolButton(self)
|
||||||
self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
|
|
||||||
self.forwardButton.setContentsMargins(0, 0, 0, 0)
|
self.forwardButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.forwardButton.setIconSize(QSize(fPx, fPx))
|
self.forwardButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.forwardButton.setFixedSize(fPx, fPx)
|
self.forwardButton.setFixedSize(fPx, fPx)
|
||||||
self.forwardButton.setStyleSheet(buttonStyle)
|
|
||||||
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.forwardButton.setVisible(False)
|
self.forwardButton.setVisible(False)
|
||||||
self.forwardButton.setToolTip(self.tr("Go forward"))
|
self.forwardButton.setToolTip(self.tr("Go forward"))
|
||||||
self.forwardButton.clicked.connect(self.docViewer.navForward)
|
self.forwardButton.clicked.connect(self.docViewer.navForward)
|
||||||
|
|
||||||
self.refreshButton = QToolButton(self)
|
self.refreshButton = QToolButton(self)
|
||||||
self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
|
|
||||||
self.refreshButton.setContentsMargins(0, 0, 0, 0)
|
self.refreshButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.refreshButton.setIconSize(QSize(fPx, fPx))
|
self.refreshButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.refreshButton.setFixedSize(fPx, fPx)
|
self.refreshButton.setFixedSize(fPx, fPx)
|
||||||
self.refreshButton.setStyleSheet(buttonStyle)
|
|
||||||
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.refreshButton.setVisible(False)
|
self.refreshButton.setVisible(False)
|
||||||
self.refreshButton.setToolTip(self.tr("Reload the document"))
|
self.refreshButton.setToolTip(self.tr("Reload the document"))
|
||||||
self.refreshButton.clicked.connect(self._refreshDocument)
|
self.refreshButton.clicked.connect(self._refreshDocument)
|
||||||
|
|
||||||
self.closeButton = QToolButton(self)
|
self.closeButton = QToolButton(self)
|
||||||
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
|
|
||||||
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.closeButton.setIconSize(QSize(fPx, fPx))
|
self.closeButton.setIconSize(QSize(fPx, fPx))
|
||||||
self.closeButton.setFixedSize(fPx, fPx)
|
self.closeButton.setFixedSize(fPx, fPx)
|
||||||
self.closeButton.setStyleSheet(buttonStyle)
|
|
||||||
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.closeButton.setVisible(False)
|
self.closeButton.setVisible(False)
|
||||||
self.closeButton.setToolTip(self.tr("Close the document"))
|
self.closeButton.setToolTip(self.tr("Close the document"))
|
||||||
@@ -803,7 +797,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
self.setMinimumHeight(fPx + 2*cM)
|
self.setMinimumHeight(fPx + 2*cM)
|
||||||
|
|
||||||
# Fix the Colours
|
# Fix the Colours
|
||||||
self.matchColours()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiDocViewHeader initialisation complete")
|
logger.debug("GuiDocViewHeader initialisation complete")
|
||||||
|
|
||||||
@@ -813,6 +807,28 @@ class GuiDocViewHeader(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.backButton.setIcon(self.mainTheme.getIcon("backward"))
|
||||||
|
self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
|
||||||
|
self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
|
||||||
|
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
|
||||||
|
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||||
|
).format(*self.mainTheme.colText)
|
||||||
|
|
||||||
|
self.backButton.setStyleSheet(buttonStyle)
|
||||||
|
self.forwardButton.setStyleSheet(buttonStyle)
|
||||||
|
self.refreshButton.setStyleSheet(buttonStyle)
|
||||||
|
self.closeButton.setStyleSheet(buttonStyle)
|
||||||
|
|
||||||
|
self.matchColours()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def matchColours(self):
|
def matchColours(self):
|
||||||
"""Update the colours of the widget to match those of the syntax
|
"""Update the colours of the widget to match those of the syntax
|
||||||
theme rather than the main GUI.
|
theme rather than the main GUI.
|
||||||
@@ -873,12 +889,14 @@ class GuiDocViewHeader(QWidget):
|
|||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _closeDocument(self):
|
def _closeDocument(self):
|
||||||
"""Trigger the close editor/viewer on the main window.
|
"""Trigger the close editor/viewer on the main window.
|
||||||
"""
|
"""
|
||||||
self.mainGui.closeDocViewer()
|
self.mainGui.closeDocViewer()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _refreshDocument(self):
|
def _refreshDocument(self):
|
||||||
"""Reload the content of the document.
|
"""Reload the content of the document.
|
||||||
"""
|
"""
|
||||||
@@ -926,33 +944,13 @@ class GuiDocViewFooter(QWidget):
|
|||||||
bSp = self.mainConf.pxInt(2)
|
bSp = self.mainConf.pxInt(2)
|
||||||
hSp = self.mainConf.pxInt(8)
|
hSp = self.mainConf.pxInt(8)
|
||||||
|
|
||||||
# Icons
|
|
||||||
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
|
|
||||||
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
|
|
||||||
stickyIcon = QIcon()
|
|
||||||
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
|
|
||||||
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
|
|
||||||
|
|
||||||
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
|
|
||||||
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
|
|
||||||
bulletIcon = QIcon()
|
|
||||||
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
|
|
||||||
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
|
|
||||||
|
|
||||||
# Main Widget Settings
|
# Main Widget Settings
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setAutoFillBackground(True)
|
self.setAutoFillBackground(True)
|
||||||
|
|
||||||
buttonStyle = (
|
|
||||||
"QToolButton {{border: none; background: transparent;}} "
|
|
||||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
|
||||||
).format(*self.mainTheme.colText)
|
|
||||||
|
|
||||||
# Show/Hide Details
|
# Show/Hide Details
|
||||||
self.showHide = QToolButton(self)
|
self.showHide = QToolButton(self)
|
||||||
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.showHide.setStyleSheet(buttonStyle)
|
|
||||||
self.showHide.setIcon(self.mainTheme.getIcon("reference"))
|
|
||||||
self.showHide.setIconSize(QSize(fPx, fPx))
|
self.showHide.setIconSize(QSize(fPx, fPx))
|
||||||
self.showHide.setFixedSize(QSize(fPx, fPx))
|
self.showHide.setFixedSize(QSize(fPx, fPx))
|
||||||
self.showHide.clicked.connect(self._doShowHide)
|
self.showHide.clicked.connect(self._doShowHide)
|
||||||
@@ -962,8 +960,6 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.stickyRefs = QToolButton(self)
|
self.stickyRefs = QToolButton(self)
|
||||||
self.stickyRefs.setCheckable(True)
|
self.stickyRefs.setCheckable(True)
|
||||||
self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.stickyRefs.setStyleSheet(buttonStyle)
|
|
||||||
self.stickyRefs.setIcon(stickyIcon)
|
|
||||||
self.stickyRefs.setIconSize(QSize(fPx, fPx))
|
self.stickyRefs.setIconSize(QSize(fPx, fPx))
|
||||||
self.stickyRefs.setFixedSize(QSize(fPx, fPx))
|
self.stickyRefs.setFixedSize(QSize(fPx, fPx))
|
||||||
self.stickyRefs.toggled.connect(self._doToggleSticky)
|
self.stickyRefs.toggled.connect(self._doToggleSticky)
|
||||||
@@ -976,8 +972,6 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.showComments.setCheckable(True)
|
self.showComments.setCheckable(True)
|
||||||
self.showComments.setChecked(self.mainConf.viewComments)
|
self.showComments.setChecked(self.mainConf.viewComments)
|
||||||
self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.showComments.setStyleSheet(buttonStyle)
|
|
||||||
self.showComments.setIcon(bulletIcon)
|
|
||||||
self.showComments.setIconSize(QSize(fPx, fPx))
|
self.showComments.setIconSize(QSize(fPx, fPx))
|
||||||
self.showComments.setFixedSize(QSize(fPx, fPx))
|
self.showComments.setFixedSize(QSize(fPx, fPx))
|
||||||
self.showComments.toggled.connect(self._doToggleComments)
|
self.showComments.toggled.connect(self._doToggleComments)
|
||||||
@@ -988,8 +982,6 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.showSynopsis.setCheckable(True)
|
self.showSynopsis.setCheckable(True)
|
||||||
self.showSynopsis.setChecked(self.mainConf.viewSynopsis)
|
self.showSynopsis.setChecked(self.mainConf.viewSynopsis)
|
||||||
self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.showSynopsis.setStyleSheet(buttonStyle)
|
|
||||||
self.showSynopsis.setIcon(bulletIcon)
|
|
||||||
self.showSynopsis.setIconSize(QSize(fPx, fPx))
|
self.showSynopsis.setIconSize(QSize(fPx, fPx))
|
||||||
self.showSynopsis.setFixedSize(QSize(fPx, fPx))
|
self.showSynopsis.setFixedSize(QSize(fPx, fPx))
|
||||||
self.showSynopsis.toggled.connect(self._doToggleSynopsis)
|
self.showSynopsis.toggled.connect(self._doToggleSynopsis)
|
||||||
@@ -1063,7 +1055,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.setMinimumHeight(fPx + 2*cM)
|
self.setMinimumHeight(fPx + 2*cM)
|
||||||
|
|
||||||
# Fix the Colours
|
# Fix the Colours
|
||||||
self.matchColours()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiDocViewFooter initialisation complete")
|
logger.debug("GuiDocViewFooter initialisation complete")
|
||||||
|
|
||||||
@@ -1073,6 +1065,46 @@ class GuiDocViewFooter(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
# Icons
|
||||||
|
|
||||||
|
fPx = int(0.9*self.mainTheme.fontPixelSize)
|
||||||
|
|
||||||
|
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
|
||||||
|
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
|
||||||
|
stickyIcon = QIcon()
|
||||||
|
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
|
||||||
|
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
|
||||||
|
|
||||||
|
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
|
||||||
|
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
|
||||||
|
bulletIcon = QIcon()
|
||||||
|
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
|
||||||
|
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
|
||||||
|
|
||||||
|
self.showHide.setIcon(self.mainTheme.getIcon("reference"))
|
||||||
|
self.stickyRefs.setIcon(stickyIcon)
|
||||||
|
self.showComments.setIcon(bulletIcon)
|
||||||
|
self.showSynopsis.setIcon(bulletIcon)
|
||||||
|
|
||||||
|
# StyleSheets
|
||||||
|
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||||
|
).format(*self.mainTheme.colText)
|
||||||
|
|
||||||
|
self.showHide.setStyleSheet(buttonStyle)
|
||||||
|
self.stickyRefs.setStyleSheet(buttonStyle)
|
||||||
|
self.showComments.setStyleSheet(buttonStyle)
|
||||||
|
self.showSynopsis.setStyleSheet(buttonStyle)
|
||||||
|
|
||||||
|
self.matchColours()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def matchColours(self):
|
def matchColours(self):
|
||||||
"""Update the colours of the widget to match those of the syntax
|
"""Update the colours of the widget to match those of the syntax
|
||||||
theme rather than the main GUI.
|
theme rather than the main GUI.
|
||||||
@@ -1094,6 +1126,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _doShowHide(self):
|
def _doShowHide(self):
|
||||||
"""Toggle the expand/collapse of the panel.
|
"""Toggle the expand/collapse of the panel.
|
||||||
"""
|
"""
|
||||||
@@ -1101,6 +1134,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.viewMeta.setVisible(not isVisible)
|
self.viewMeta.setVisible(not isVisible)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleSticky(self, theState):
|
def _doToggleSticky(self, theState):
|
||||||
"""Toggle the sticky flag for the reference panel.
|
"""Toggle the sticky flag for the reference panel.
|
||||||
"""
|
"""
|
||||||
@@ -1110,6 +1144,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.viewMeta.refreshReferences(self.docViewer.docHandle())
|
self.viewMeta.refreshReferences(self.docViewer.docHandle())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleComments(self, theState):
|
def _doToggleComments(self, theState):
|
||||||
"""Toggle the view comment button and reload the document.
|
"""Toggle the view comment button and reload the document.
|
||||||
"""
|
"""
|
||||||
@@ -1117,6 +1152,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
self.docViewer.reloadText()
|
self.docViewer.reloadText()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
def _doToggleSynopsis(self, theState):
|
def _doToggleSynopsis(self, theState):
|
||||||
"""Toggle the view synopsis button and reload the document.
|
"""Toggle the view synopsis button and reload the document.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -53,12 +53,8 @@ class GuiItemDetails(QWidget):
|
|||||||
hSp = self.mainConf.pxInt(6)
|
hSp = self.mainConf.pxInt(6)
|
||||||
vSp = self.mainConf.pxInt(1)
|
vSp = self.mainConf.pxInt(1)
|
||||||
mPx = self.mainConf.pxInt(6)
|
mPx = self.mainConf.pxInt(6)
|
||||||
iPx = self.mainTheme.baseIconSize
|
|
||||||
fPt = self.mainTheme.fontPointSize
|
fPt = self.mainTheme.fontPointSize
|
||||||
|
|
||||||
self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
|
|
||||||
self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
|
|
||||||
|
|
||||||
fntLabel = QFont()
|
fntLabel = QFont()
|
||||||
fntLabel.setBold(True)
|
fntLabel.setBold(True)
|
||||||
fntLabel.setPointSizeF(0.9*fPt)
|
fntLabel.setPointSizeF(0.9*fPt)
|
||||||
@@ -179,6 +175,8 @@ class GuiItemDetails(QWidget):
|
|||||||
|
|
||||||
self.setLayout(self.mainBox)
|
self.setLayout(self.mainBox)
|
||||||
|
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
# Make sure the columns for flags and counts don't resize too often
|
# Make sure the columns for flags and counts don't resize too often
|
||||||
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
|
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
|
||||||
countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
|
countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
|
||||||
@@ -219,6 +217,15 @@ class GuiItemDetails(QWidget):
|
|||||||
"""
|
"""
|
||||||
self.updateViewBox(self._itemHandle)
|
self.updateViewBox(self._itemHandle)
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
|
||||||
|
self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
|
||||||
|
self.updateViewBox(self._itemHandle)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Public Slots
|
# Public Slots
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -554,6 +554,15 @@ class GuiMainMenu(QMenuBar):
|
|||||||
)
|
)
|
||||||
self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0])
|
self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0])
|
||||||
|
|
||||||
|
# Insert > Special Comments
|
||||||
|
self.mInsComments = self.insertMenu.addMenu(self.tr("Special Comments"))
|
||||||
|
|
||||||
|
# Insert > Synopsis Comment
|
||||||
|
self.aInsSynopsis = QAction(self.tr("Synopsis Comment"), self)
|
||||||
|
self.aInsSynopsis.setShortcut("Ctrl+K, S")
|
||||||
|
self.aInsSynopsis.triggered.connect(lambda: self._docInsert(nwDocInsert.SYNOPSIS))
|
||||||
|
self.mInsComments.addAction(self.aInsSynopsis)
|
||||||
|
|
||||||
# Insert > Symbols
|
# Insert > Symbols
|
||||||
self.mInsBreaks = self.insertMenu.addMenu(self.tr("Page Break and Space"))
|
self.mInsBreaks = self.insertMenu.addMenu(self.tr("Page Break and Space"))
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,14 @@ class GuiNovelView(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.novelBar.updateTheme()
|
||||||
|
self.novelTree.updateTheme()
|
||||||
|
self.refreshTree()
|
||||||
|
return
|
||||||
|
|
||||||
def initSettings(self):
|
def initSettings(self):
|
||||||
"""Initialise GUI elements that depend on specific settings.
|
"""Initialise GUI elements that depend on specific settings.
|
||||||
"""
|
"""
|
||||||
@@ -181,16 +189,6 @@ class GuiNovelToolBar(QWidget):
|
|||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setAutoFillBackground(True)
|
self.setAutoFillBackground(True)
|
||||||
|
|
||||||
qPalette = self.palette()
|
|
||||||
qPalette.setBrush(QPalette.Window, qPalette.base())
|
|
||||||
self.setPalette(qPalette)
|
|
||||||
|
|
||||||
fadeCol = qPalette.text().color()
|
|
||||||
buttonStyle = (
|
|
||||||
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
|
||||||
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
|
||||||
).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
|
||||||
|
|
||||||
# Widget Label
|
# Widget Label
|
||||||
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Novel Outline"))
|
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Novel Outline"))
|
||||||
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
||||||
@@ -199,9 +197,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
# Refresh Button
|
# Refresh Button
|
||||||
self.tbRefresh = QToolButton(self)
|
self.tbRefresh = QToolButton(self)
|
||||||
self.tbRefresh.setToolTip(self.tr("Refresh"))
|
self.tbRefresh.setToolTip(self.tr("Refresh"))
|
||||||
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
|
|
||||||
self.tbRefresh.setIconSize(QSize(iPx, iPx))
|
self.tbRefresh.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbRefresh.setStyleSheet(buttonStyle)
|
|
||||||
self.tbRefresh.clicked.connect(self._refreshNovelTree)
|
self.tbRefresh.clicked.connect(self._refreshNovelTree)
|
||||||
|
|
||||||
# Novel Root Menu
|
# Novel Root Menu
|
||||||
@@ -211,9 +207,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
|
|
||||||
self.tbRoot = QToolButton(self)
|
self.tbRoot = QToolButton(self)
|
||||||
self.tbRoot.setToolTip(self.tr("Novel Root"))
|
self.tbRoot.setToolTip(self.tr("Novel Root"))
|
||||||
self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
|
|
||||||
self.tbRoot.setIconSize(QSize(iPx, iPx))
|
self.tbRoot.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbRoot.setStyleSheet(buttonStyle)
|
|
||||||
self.tbRoot.setMenu(self.mRoot)
|
self.tbRoot.setMenu(self.mRoot)
|
||||||
self.tbRoot.setPopupMode(QToolButton.InstantPopup)
|
self.tbRoot.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
@@ -230,9 +224,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
|
|
||||||
self.tbMore = QToolButton(self)
|
self.tbMore = QToolButton(self)
|
||||||
self.tbMore.setToolTip(self.tr("More Options"))
|
self.tbMore.setToolTip(self.tr("More Options"))
|
||||||
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
|
|
||||||
self.tbMore.setIconSize(QSize(iPx, iPx))
|
self.tbMore.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbMore.setStyleSheet(buttonStyle)
|
|
||||||
self.tbMore.setMenu(self.mMore)
|
self.tbMore.setMenu(self.mMore)
|
||||||
self.tbMore.setPopupMode(QToolButton.InstantPopup)
|
self.tbMore.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
@@ -247,6 +239,8 @@ class GuiNovelToolBar(QWidget):
|
|||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiNovelToolBar initialisation complete")
|
logger.debug("GuiNovelToolBar initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -255,6 +249,31 @@ class GuiNovelToolBar(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
# Icons
|
||||||
|
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
|
||||||
|
self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
|
||||||
|
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
|
||||||
|
|
||||||
|
qPalette = self.palette()
|
||||||
|
qPalette.setBrush(QPalette.Window, qPalette.base())
|
||||||
|
self.setPalette(qPalette)
|
||||||
|
|
||||||
|
# StyleSheets
|
||||||
|
fadeCol = qPalette.text().color()
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
||||||
|
).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||||
|
|
||||||
|
self.tbRefresh.setStyleSheet(buttonStyle)
|
||||||
|
self.tbRoot.setStyleSheet(buttonStyle)
|
||||||
|
self.tbMore.setStyleSheet(buttonStyle)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def clearContent(self):
|
def clearContent(self):
|
||||||
"""Run clearing project tasks.
|
"""Run clearing project tasks.
|
||||||
"""
|
"""
|
||||||
@@ -390,7 +409,6 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
fH2.setBold(True)
|
fH2.setBold(True)
|
||||||
|
|
||||||
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
|
||||||
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
|
|
||||||
|
|
||||||
# Connect signals
|
# Connect signals
|
||||||
self.clicked.connect(self._treeItemClicked)
|
self.clicked.connect(self._treeItemClicked)
|
||||||
@@ -399,6 +417,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
# Set custom settings
|
# Set custom settings
|
||||||
self.initSettings()
|
self.initSettings()
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiNovelTree initialisation complete")
|
logger.debug("GuiNovelTree initialisation complete")
|
||||||
|
|
||||||
@@ -420,6 +439,13 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -97,6 +97,13 @@ class GuiOutlineView(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.outlineBar.updateTheme()
|
||||||
|
self.refreshTree()
|
||||||
|
return
|
||||||
|
|
||||||
def initSettings(self):
|
def initSettings(self):
|
||||||
"""Initialise GUI elements that depend on specific settings.
|
"""Initialise GUI elements that depend on specific settings.
|
||||||
"""
|
"""
|
||||||
@@ -213,7 +220,6 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
self.setMovable(False)
|
self.setMovable(False)
|
||||||
self.setIconSize(QSize(iPx, iPx))
|
self.setIconSize(QSize(iPx, iPx))
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setStyleSheet("QToolBar {border: 0px;}")
|
|
||||||
|
|
||||||
stretch = QWidget(self)
|
stretch = QWidget(self)
|
||||||
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
@@ -228,7 +234,6 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
self.aRefresh = QAction(self.tr("Refresh"), self)
|
self.aRefresh = QAction(self.tr("Refresh"), self)
|
||||||
self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
|
|
||||||
self.aRefresh.triggered.connect(self._refreshRequested)
|
self.aRefresh.triggered.connect(self._refreshRequested)
|
||||||
|
|
||||||
# Column Menu
|
# Column Menu
|
||||||
@@ -238,7 +243,6 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.tbColumns = QToolButton(self)
|
self.tbColumns = QToolButton(self)
|
||||||
self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
|
|
||||||
self.tbColumns.setMenu(self.mColumns)
|
self.tbColumns.setMenu(self.mColumns)
|
||||||
self.tbColumns.setPopupMode(QToolButton.InstantPopup)
|
self.tbColumns.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
@@ -250,6 +254,8 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
self.addWidget(self.tbColumns)
|
self.addWidget(self.tbColumns)
|
||||||
self.addWidget(stretch)
|
self.addWidget(stretch)
|
||||||
|
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiOutlineToolBar initialisation complete")
|
logger.debug("GuiOutlineToolBar initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -258,6 +264,16 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.setStyleSheet("QToolBar {border: 0px;}")
|
||||||
|
|
||||||
|
self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
|
||||||
|
self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def populateNovelList(self):
|
def populateNovelList(self):
|
||||||
"""Fill the novel combo box with a list of all novel folders.
|
"""Fill the novel combo box with a list of all novel folders.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter.core import DocMerger, DocSplitter
|
from novelwriter.core import DocMerger, DocSplitter
|
||||||
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
|
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
|
||||||
from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel
|
from novelwriter.dialogs import GuiDocMerge, GuiDocSplit, GuiEditLabel, GuiProjectSettings
|
||||||
from novelwriter.constants import nwHeaders, trConst, nwLabels
|
from novelwriter.constants import nwHeaders, nwUnicode, trConst, nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -62,6 +62,9 @@ class GuiProjectView(QWidget):
|
|||||||
selectedItemChanged = pyqtSignal(str)
|
selectedItemChanged = pyqtSignal(str)
|
||||||
openDocumentRequest = pyqtSignal(str, Enum, int, str)
|
openDocumentRequest = pyqtSignal(str, Enum, int, str)
|
||||||
|
|
||||||
|
# Requests for the main GUI
|
||||||
|
projectSettingsRequest = pyqtSignal(int)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
@@ -102,9 +105,6 @@ class GuiProjectView(QWidget):
|
|||||||
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
|
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
|
||||||
|
|
||||||
# Function Mappings
|
# Function Mappings
|
||||||
self.revealNewTreeItem = self.projTree.revealNewTreeItem
|
|
||||||
self.renameTreeItem = self.projTree.renameTreeItem
|
|
||||||
self.getTreeFromHandle = self.projTree.getTreeFromHandle
|
|
||||||
self.emptyTrash = self.projTree.emptyTrash
|
self.emptyTrash = self.projTree.emptyTrash
|
||||||
self.requestDeleteItem = self.projTree.requestDeleteItem
|
self.requestDeleteItem = self.projTree.requestDeleteItem
|
||||||
self.setTreeItemValues = self.projTree.setTreeItemValues
|
self.setTreeItemValues = self.projTree.setTreeItemValues
|
||||||
@@ -119,6 +119,13 @@ class GuiProjectView(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
self.projBar.updateTheme()
|
||||||
|
self.populateTree()
|
||||||
|
return
|
||||||
|
|
||||||
def initSettings(self):
|
def initSettings(self):
|
||||||
"""Initialise GUI elements that depend on specific settings.
|
"""Initialise GUI elements that depend on specific settings.
|
||||||
"""
|
"""
|
||||||
@@ -161,6 +168,16 @@ class GuiProjectView(QWidget):
|
|||||||
"""
|
"""
|
||||||
return self.projTree.hasFocus()
|
return self.projTree.hasFocus()
|
||||||
|
|
||||||
|
def renameTreeItem(self, tHandle=None):
|
||||||
|
"""External request to rename an item or the currently selected
|
||||||
|
item. This is triggered by the global menu or keyboard shortcut.
|
||||||
|
"""
|
||||||
|
if tHandle is None:
|
||||||
|
tHandle = self.projTree.getSelectedHandle()
|
||||||
|
if tHandle:
|
||||||
|
return self.projTree.renameTreeItem(tHandle)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Public Slots
|
# Public Slots
|
||||||
##
|
##
|
||||||
@@ -203,16 +220,6 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setAutoFillBackground(True)
|
self.setAutoFillBackground(True)
|
||||||
|
|
||||||
qPalette = self.palette()
|
|
||||||
qPalette.setBrush(QPalette.Window, qPalette.base())
|
|
||||||
self.setPalette(qPalette)
|
|
||||||
|
|
||||||
fadeCol = qPalette.text().color()
|
|
||||||
buttonStyle = (
|
|
||||||
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
|
||||||
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
|
||||||
).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
|
||||||
|
|
||||||
# Widget Label
|
# Widget Label
|
||||||
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Project Content"))
|
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Project Content"))
|
||||||
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
||||||
@@ -224,78 +231,56 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.tbQuick = QToolButton(self)
|
self.tbQuick = QToolButton(self)
|
||||||
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
|
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
|
||||||
self.tbQuick.setShortcut("Ctrl+L")
|
self.tbQuick.setShortcut("Ctrl+L")
|
||||||
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
|
|
||||||
self.tbQuick.setIconSize(QSize(iPx, iPx))
|
self.tbQuick.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbQuick.setStyleSheet(buttonStyle)
|
|
||||||
self.tbQuick.setMenu(self.mQuick)
|
self.tbQuick.setMenu(self.mQuick)
|
||||||
self.tbQuick.setPopupMode(QToolButton.InstantPopup)
|
self.tbQuick.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
# Move Buttons
|
# Move Buttons
|
||||||
self.tbMoveU = QToolButton(self)
|
self.tbMoveU = QToolButton(self)
|
||||||
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
|
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
|
||||||
self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
|
|
||||||
self.tbMoveU.setIconSize(QSize(iPx, iPx))
|
self.tbMoveU.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbMoveU.setStyleSheet(buttonStyle)
|
|
||||||
self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1))
|
self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1))
|
||||||
|
|
||||||
self.tbMoveD = QToolButton(self)
|
self.tbMoveD = QToolButton(self)
|
||||||
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
|
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
|
||||||
self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
|
|
||||||
self.tbMoveD.setIconSize(QSize(iPx, iPx))
|
self.tbMoveD.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbMoveD.setStyleSheet(buttonStyle)
|
|
||||||
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
|
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
|
||||||
|
|
||||||
# Add Item Menu
|
# Add Item Menu
|
||||||
self.mAdd = QMenu()
|
self.mAdd = QMenu()
|
||||||
|
|
||||||
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
|
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
|
||||||
self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
|
|
||||||
self.aAddEmpty.triggered.connect(
|
self.aAddEmpty.triggered.connect(
|
||||||
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False)
|
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
|
self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
|
||||||
self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
|
|
||||||
self.aAddChap.triggered.connect(
|
self.aAddChap.triggered.connect(
|
||||||
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False)
|
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
|
self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
|
||||||
self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
|
|
||||||
self.aAddScene.triggered.connect(
|
self.aAddScene.triggered.connect(
|
||||||
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False)
|
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
|
self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
|
||||||
self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
|
|
||||||
self.aAddNote.triggered.connect(
|
self.aAddNote.triggered.connect(
|
||||||
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
|
self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
|
||||||
self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
|
|
||||||
self.aAddFolder.triggered.connect(
|
self.aAddFolder.triggered.connect(
|
||||||
lambda: self.projTree.newTreeItem(nwItemType.FOLDER)
|
lambda: self.projTree.newTreeItem(nwItemType.FOLDER)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
|
self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
|
||||||
self._addRootFolderEntry(nwItemClass.NOVEL)
|
self._buildRootMenu()
|
||||||
self._addRootFolderEntry(nwItemClass.ARCHIVE)
|
|
||||||
self.mAddRoot.addSeparator()
|
|
||||||
self._addRootFolderEntry(nwItemClass.PLOT)
|
|
||||||
self._addRootFolderEntry(nwItemClass.CHARACTER)
|
|
||||||
self._addRootFolderEntry(nwItemClass.WORLD)
|
|
||||||
self._addRootFolderEntry(nwItemClass.TIMELINE)
|
|
||||||
self._addRootFolderEntry(nwItemClass.OBJECT)
|
|
||||||
self._addRootFolderEntry(nwItemClass.ENTITY)
|
|
||||||
self._addRootFolderEntry(nwItemClass.CUSTOM)
|
|
||||||
|
|
||||||
self.tbAdd = QToolButton(self)
|
self.tbAdd = QToolButton(self)
|
||||||
self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
|
self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
|
||||||
self.tbAdd.setShortcut("Ctrl+N")
|
self.tbAdd.setShortcut("Ctrl+N")
|
||||||
self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
|
|
||||||
self.tbAdd.setIconSize(QSize(iPx, iPx))
|
self.tbAdd.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbAdd.setStyleSheet(buttonStyle)
|
|
||||||
self.tbAdd.setMenu(self.mAdd)
|
self.tbAdd.setMenu(self.mAdd)
|
||||||
self.tbAdd.setPopupMode(QToolButton.InstantPopup)
|
self.tbAdd.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
@@ -316,9 +301,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
|
|
||||||
self.tbMore = QToolButton(self)
|
self.tbMore = QToolButton(self)
|
||||||
self.tbMore.setToolTip(self.tr("More Options"))
|
self.tbMore.setToolTip(self.tr("More Options"))
|
||||||
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
|
|
||||||
self.tbMore.setIconSize(QSize(iPx, iPx))
|
self.tbMore.setIconSize(QSize(iPx, iPx))
|
||||||
self.tbMore.setStyleSheet(buttonStyle)
|
|
||||||
self.tbMore.setMenu(self.mMore)
|
self.tbMore.setMenu(self.mMore)
|
||||||
self.tbMore.setPopupMode(QToolButton.InstantPopup)
|
self.tbMore.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
@@ -334,6 +317,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.outerBox.setSpacing(0)
|
self.outerBox.setSpacing(0)
|
||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiProjectToolBar initialisation complete")
|
logger.debug("GuiProjectToolBar initialisation complete")
|
||||||
|
|
||||||
@@ -343,6 +327,41 @@ class GuiProjectToolBar(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
qPalette = self.palette()
|
||||||
|
qPalette.setBrush(QPalette.Window, qPalette.base())
|
||||||
|
self.setPalette(qPalette)
|
||||||
|
|
||||||
|
fadeCol = qPalette.text().color()
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
||||||
|
).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||||
|
|
||||||
|
self.tbQuick.setStyleSheet(buttonStyle)
|
||||||
|
self.tbMoveU.setStyleSheet(buttonStyle)
|
||||||
|
self.tbMoveD.setStyleSheet(buttonStyle)
|
||||||
|
self.tbAdd.setStyleSheet(buttonStyle)
|
||||||
|
self.tbMore.setStyleSheet(buttonStyle)
|
||||||
|
|
||||||
|
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
|
||||||
|
self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
|
||||||
|
self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
|
||||||
|
self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
|
||||||
|
self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
|
||||||
|
self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
|
||||||
|
self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
|
||||||
|
self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
|
||||||
|
self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
|
||||||
|
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
|
||||||
|
|
||||||
|
self.buildQuickLinkMenu()
|
||||||
|
self._buildRootMenu()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def clearContent(self):
|
def clearContent(self):
|
||||||
"""Clear dynamic content on the tool bar.
|
"""Clear dynamic content on the tool bar.
|
||||||
"""
|
"""
|
||||||
@@ -369,24 +388,44 @@ class GuiProjectToolBar(QWidget):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _addRootFolderEntry(self, itemClass):
|
def _buildRootMenu(self):
|
||||||
"""Add a menu entry for a root folder of a given class.
|
"""Build the rood folder menu.
|
||||||
"""
|
"""
|
||||||
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
|
def addClass(itemClass):
|
||||||
aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass]))
|
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
|
||||||
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
|
aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass]))
|
||||||
self.mAddRoot.addAction(aNew)
|
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
|
||||||
|
self.mAddRoot.addAction(aNew)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.mAddRoot.clear()
|
||||||
|
addClass(nwItemClass.NOVEL)
|
||||||
|
addClass(nwItemClass.ARCHIVE)
|
||||||
|
self.mAddRoot.addSeparator()
|
||||||
|
addClass(nwItemClass.PLOT)
|
||||||
|
addClass(nwItemClass.CHARACTER)
|
||||||
|
addClass(nwItemClass.WORLD)
|
||||||
|
addClass(nwItemClass.TIMELINE)
|
||||||
|
addClass(nwItemClass.OBJECT)
|
||||||
|
addClass(nwItemClass.ENTITY)
|
||||||
|
addClass(nwItemClass.CUSTOM)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# END Class GuiProjectToolBar
|
# END Class GuiProjectToolBar
|
||||||
|
|
||||||
|
|
||||||
class GuiProjectTree(QTreeWidget):
|
class GuiProjectTree(QTreeWidget):
|
||||||
|
|
||||||
|
C_DATA = 0
|
||||||
C_NAME = 0
|
C_NAME = 0
|
||||||
C_COUNT = 1
|
C_COUNT = 1
|
||||||
C_ACTIVE = 2
|
C_ACTIVE = 2
|
||||||
C_STATUS = 3
|
C_STATUS = 3
|
||||||
|
|
||||||
|
D_HANDLE = Qt.UserRole
|
||||||
|
D_WORDS = Qt.UserRole + 1
|
||||||
|
|
||||||
def __init__(self, projView):
|
def __init__(self, projView):
|
||||||
super().__init__(parent=projView)
|
super().__init__(parent=projView)
|
||||||
|
|
||||||
@@ -912,10 +951,13 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
trItem.setToolTip(self.C_STATUS, itemStatus)
|
trItem.setToolTip(self.C_STATUS, itemStatus)
|
||||||
|
|
||||||
if nwItem.isFileType():
|
if nwItem.isFileType():
|
||||||
iconName = "check" if nwItem.isActive else "cross"
|
iconName = "checked" if nwItem.isActive else "unchecked"
|
||||||
toolTip = self._lblActive if nwItem.isActive else self._lblInactive
|
toolTip = self._lblActive if nwItem.isActive else self._lblInactive
|
||||||
trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName))
|
|
||||||
trItem.setToolTip(self.C_ACTIVE, toolTip)
|
trItem.setToolTip(self.C_ACTIVE, toolTip)
|
||||||
|
else:
|
||||||
|
iconName = "noncheckable"
|
||||||
|
|
||||||
|
trItem.setIcon(self.C_ACTIVE, self.mainTheme.getIcon(iconName))
|
||||||
|
|
||||||
if self.mainConf.emphLabels and nwItem.isDocumentLayout():
|
if self.mainConf.emphLabels and nwItem.isDocumentLayout():
|
||||||
trFont = trItem.font(self.C_NAME)
|
trFont = trItem.font(self.C_NAME)
|
||||||
@@ -939,10 +981,10 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
if countChildren:
|
if countChildren:
|
||||||
for i in range(tItem.childCount()):
|
for i in range(tItem.childCount()):
|
||||||
newCount += int(tItem.child(i).data(self.C_COUNT, Qt.UserRole))
|
newCount += int(tItem.child(i).data(self.C_DATA, self.D_WORDS))
|
||||||
|
|
||||||
tItem.setText(self.C_COUNT, f"{newCount:n}")
|
tItem.setText(self.C_COUNT, f"{newCount:n}")
|
||||||
tItem.setData(self.C_COUNT, Qt.UserRole, int(newCount))
|
tItem.setData(self.C_DATA, self.D_WORDS, int(newCount))
|
||||||
|
|
||||||
pItem = tItem.parent()
|
pItem = tItem.parent()
|
||||||
if pItem is None:
|
if pItem is None:
|
||||||
@@ -951,8 +993,8 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
pCount = 0
|
pCount = 0
|
||||||
pHandle = None
|
pHandle = None
|
||||||
for i in range(pItem.childCount()):
|
for i in range(pItem.childCount()):
|
||||||
pCount += int(pItem.child(i).data(self.C_COUNT, Qt.UserRole))
|
pCount += int(pItem.child(i).data(self.C_DATA, self.D_WORDS))
|
||||||
pHandle = pItem.data(self.C_NAME, Qt.UserRole)
|
pHandle = pItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
|
|
||||||
if pHandle:
|
if pHandle:
|
||||||
if self.theProject.tree.checkType(pHandle, nwItemType.FILE):
|
if self.theProject.tree.checkType(pHandle, nwItemType.FILE):
|
||||||
@@ -1003,8 +1045,8 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
dstIndex = min(max(0, dstIndex), dstItem.childCount())
|
dstIndex = min(max(0, dstIndex), dstItem.childCount())
|
||||||
sHandle = srcItem.data(self.C_NAME, Qt.UserRole)
|
sHandle = srcItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
dHandle = dstItem.data(self.C_NAME, Qt.UserRole)
|
dHandle = dstItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
logger.debug("Moving item '%s' back to '%s', index %d", sHandle, dHandle, dstIndex)
|
logger.debug("Moving item '%s' back to '%s', index %d", sHandle, dHandle, dstIndex)
|
||||||
|
|
||||||
wCount = self._getItemWordCount(sHandle)
|
wCount = self._getItemWordCount(sHandle)
|
||||||
@@ -1028,7 +1070,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
"""
|
"""
|
||||||
selItem = self.selectedItems()
|
selItem = self.selectedItems()
|
||||||
if selItem:
|
if selItem:
|
||||||
return selItem[0].data(self.C_NAME, Qt.UserRole)
|
return selItem[0].data(self.C_DATA, self.D_HANDLE)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1111,10 +1153,12 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
open a context menu in-place.
|
open a context menu in-place.
|
||||||
"""
|
"""
|
||||||
tItem = None
|
tItem = None
|
||||||
|
hasChild = False
|
||||||
selItem = self.itemAt(clickPos)
|
selItem = self.itemAt(clickPos)
|
||||||
if isinstance(selItem, QTreeWidgetItem):
|
if isinstance(selItem, QTreeWidgetItem):
|
||||||
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
|
tHandle = selItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
tItem = self.theProject.tree[tHandle]
|
tItem = self.theProject.tree[tHandle]
|
||||||
|
hasChild = selItem.childCount() > 0
|
||||||
|
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
logger.debug("No item found")
|
logger.debug("No item found")
|
||||||
@@ -1128,9 +1172,8 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
trashHandle = self.theProject.tree.trashRoot()
|
trashHandle = self.theProject.tree.trashRoot()
|
||||||
if tItem.itemHandle == trashHandle and trashHandle is not None:
|
if tItem.itemHandle == trashHandle and trashHandle is not None:
|
||||||
# The trash folder only has one option
|
# The trash folder only has one option
|
||||||
ctxMenu.addAction(
|
aEmptyTrash = ctxMenu.addAction(self.tr("Empty Trash"))
|
||||||
self.tr("Empty Trash"), lambda: self.emptyTrash()
|
aEmptyTrash.triggered.connect(lambda: self.emptyTrash())
|
||||||
)
|
|
||||||
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1140,15 +1183,14 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
isRoot = tItem.isRootType()
|
isRoot = tItem.isRootType()
|
||||||
isFolder = tItem.isFolderType()
|
isFolder = tItem.isFolderType()
|
||||||
isFile = tItem.isFileType()
|
isFile = tItem.isFileType()
|
||||||
hasChild = selItem.childCount() > 0
|
|
||||||
|
|
||||||
if isFile:
|
if isFile:
|
||||||
ctxMenu.addAction(
|
aOpenDoc = ctxMenu.addAction(self.tr("Open Document"))
|
||||||
self.tr("Open Document"),
|
aOpenDoc.triggered.connect(
|
||||||
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
|
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, -1, "")
|
||||||
)
|
)
|
||||||
ctxMenu.addAction(
|
aViewDoc = ctxMenu.addAction(self.tr("View Document"))
|
||||||
self.tr("View Document"),
|
aViewDoc.triggered.connect(
|
||||||
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
|
lambda: self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, -1, "")
|
||||||
)
|
)
|
||||||
ctxMenu.addSeparator()
|
ctxMenu.addSeparator()
|
||||||
@@ -1156,29 +1198,40 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
# Edit Item Settings
|
# Edit Item Settings
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
ctxMenu.addAction(
|
aLabel = ctxMenu.addAction(self.tr("Change Label"))
|
||||||
self.tr("Change Label"), lambda: self.renameTreeItem(tHandle)
|
aLabel.triggered.connect(lambda: self.renameTreeItem(tHandle))
|
||||||
)
|
|
||||||
|
|
||||||
if isFile:
|
if isFile:
|
||||||
ctxMenu.addAction(
|
aActive = ctxMenu.addAction(self.tr("Toggle Active"))
|
||||||
self.tr("Toggle Active"), lambda: self._toggleItemActive(tHandle)
|
aActive.triggered.connect(lambda: self._toggleItemActive(tHandle))
|
||||||
)
|
|
||||||
|
|
||||||
|
checkMark = f" ({nwUnicode.U_CHECK})"
|
||||||
if tItem.isNovelLike():
|
if tItem.isNovelLike():
|
||||||
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
|
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
|
||||||
for n, (key, entry) in enumerate(self.theProject.statusItems.items()):
|
for n, (key, entry) in enumerate(self.theProject.statusItems.items()):
|
||||||
aStatus = mStatus.addAction(entry["icon"], entry["name"])
|
entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "")
|
||||||
|
aStatus = mStatus.addAction(entry["icon"], entryName)
|
||||||
aStatus.triggered.connect(
|
aStatus.triggered.connect(
|
||||||
lambda n, key=key: self._changeItemStatus(tHandle, key)
|
lambda n, key=key: self._changeItemStatus(tHandle, key)
|
||||||
)
|
)
|
||||||
|
mStatus.addSeparator()
|
||||||
|
aManage1 = mStatus.addAction("Manage Labels ...")
|
||||||
|
aManage1.triggered.connect(
|
||||||
|
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.TAB_STATUS)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
|
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
|
||||||
for n, (key, entry) in enumerate(self.theProject.importItems.items()):
|
for n, (key, entry) in enumerate(self.theProject.importItems.items()):
|
||||||
aImport = mImport.addAction(entry["icon"], entry["name"])
|
entryName = entry["name"] + (checkMark if tItem.itemImport == key else "")
|
||||||
|
aImport = mImport.addAction(entry["icon"], entryName)
|
||||||
aImport.triggered.connect(
|
aImport.triggered.connect(
|
||||||
lambda n, key=key: self._changeItemImport(tHandle, key)
|
lambda n, key=key: self._changeItemImport(tHandle, key)
|
||||||
)
|
)
|
||||||
|
mImport.addSeparator()
|
||||||
|
aManage2 = mImport.addAction("Manage Labels ...")
|
||||||
|
aManage2.triggered.connect(
|
||||||
|
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.TAB_IMPORT)
|
||||||
|
)
|
||||||
|
|
||||||
# Transform Item
|
# Transform Item
|
||||||
# ==============
|
# ==============
|
||||||
@@ -1193,38 +1246,30 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
isNoteFile = isFile and tItem.isNoteLayout()
|
isNoteFile = isFile and tItem.isNoteLayout()
|
||||||
|
|
||||||
if (isNoteFile or isFolder) and tItem.documentAllowed():
|
if (isNoteFile or isFolder) and tItem.documentAllowed():
|
||||||
mTrans.addAction(
|
aConvert1 = mTrans.addAction(self.tr("Convert to {0}").format(trDoc))
|
||||||
self.tr("Convert to {0}").format(trDoc),
|
aConvert1.triggered.connect(
|
||||||
lambda: self._changeItemLayout(tHandle, nwItemLayout.DOCUMENT)
|
lambda: self._changeItemLayout(tHandle, nwItemLayout.DOCUMENT)
|
||||||
)
|
)
|
||||||
|
|
||||||
if isDocFile or isFolder:
|
if isDocFile or isFolder:
|
||||||
mTrans.addAction(
|
aConvert2 = mTrans.addAction(self.tr("Convert to {0}").format(trNote))
|
||||||
self.tr("Convert to {0}").format(trNote),
|
aConvert2.triggered.connect(
|
||||||
lambda: self._changeItemLayout(tHandle, nwItemLayout.NOTE)
|
lambda: self._changeItemLayout(tHandle, nwItemLayout.NOTE)
|
||||||
)
|
)
|
||||||
|
|
||||||
if hasChild and isFile:
|
if hasChild and isFile:
|
||||||
mTrans.addAction(
|
aMerge1 = mTrans.addAction(self.tr("Merge Child Items into Self"))
|
||||||
self.tr("Merge Child Items into Self"),
|
aMerge1.triggered.connect(lambda: self._mergeDocuments(tHandle, False))
|
||||||
lambda: self._mergeDocuments(tHandle, False)
|
aMerge2 = mTrans.addAction(self.tr("Merge Child Items into New"))
|
||||||
)
|
aMerge2.triggered.connect(lambda: self._mergeDocuments(tHandle, True))
|
||||||
mTrans.addAction(
|
|
||||||
self.tr("Merge Child Items into New"),
|
|
||||||
lambda: self._mergeDocuments(tHandle, True)
|
|
||||||
)
|
|
||||||
|
|
||||||
if hasChild and isFolder:
|
if hasChild and isFolder:
|
||||||
mTrans.addAction(
|
aMerge3 = mTrans.addAction(self.tr("Merge Documents in Folder"))
|
||||||
self.tr("Merge Documents in Folder"),
|
aMerge3.triggered.connect(lambda: self._mergeDocuments(tHandle, True))
|
||||||
lambda: self._mergeDocuments(tHandle, True)
|
|
||||||
)
|
|
||||||
|
|
||||||
if isFile:
|
if isFile:
|
||||||
mTrans.addAction(
|
aSplit1 = mTrans.addAction(self.tr("Split Document by Headers"))
|
||||||
self.tr("Split Document by Headers"),
|
aSplit1.triggered.connect(lambda: self._splitDocument(tHandle))
|
||||||
lambda: self._splitDocument(tHandle)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Expand/Collapse/Delete
|
# Expand/Collapse/Delete
|
||||||
# ======================
|
# ======================
|
||||||
@@ -1232,23 +1277,17 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
ctxMenu.addSeparator()
|
ctxMenu.addSeparator()
|
||||||
|
|
||||||
if hasChild:
|
if hasChild:
|
||||||
ctxMenu.addAction(
|
aExpand = ctxMenu.addAction(self.tr("Expand All"))
|
||||||
self.tr("Expand All"),
|
aExpand.triggered.connect(lambda: self.setExpandedFromHandle(tHandle, True))
|
||||||
lambda: self.setExpandedFromHandle(tHandle, True)
|
aCollapse = ctxMenu.addAction(self.tr("Collapse All"))
|
||||||
)
|
aCollapse.triggered.connect(lambda: self.setExpandedFromHandle(tHandle, False))
|
||||||
ctxMenu.addAction(
|
|
||||||
self.tr("Collapse All"),
|
|
||||||
lambda: self.setExpandedFromHandle(tHandle, False)
|
|
||||||
)
|
|
||||||
|
|
||||||
if tItem.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
|
if tItem.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
|
||||||
ctxMenu.addAction(
|
aDelete = ctxMenu.addAction(self.tr("Delete Permanently"))
|
||||||
self.tr("Delete Permanently"), lambda: self.permanentlyDeleteItem(tHandle)
|
aDelete.triggered.connect(lambda: self.permanentlyDeleteItem(tHandle))
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
ctxMenu.addAction(
|
aMoveTrash = ctxMenu.addAction(self.tr("Move to Trash"))
|
||||||
self.tr("Move to Trash"), lambda: self.moveItemToTrash(tHandle)
|
aMoveTrash.triggered.connect(lambda: self.moveItemToTrash(tHandle))
|
||||||
)
|
|
||||||
|
|
||||||
# Show Context Menu
|
# Show Context Menu
|
||||||
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
|
||||||
@@ -1276,7 +1315,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
if not isinstance(selItem, QTreeWidgetItem):
|
if not isinstance(selItem, QTreeWidgetItem):
|
||||||
return
|
return
|
||||||
|
|
||||||
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
|
tHandle = selItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
tItem = self.theProject.tree[tHandle]
|
tItem = self.theProject.tree[tHandle]
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
return
|
return
|
||||||
@@ -1335,7 +1374,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
# Update item parent handle in the project, make sure meta data
|
# Update item parent handle in the project, make sure meta data
|
||||||
# is updated accordingly, and update word count
|
# is updated accordingly, and update word count
|
||||||
pHandle = trItemP.data(self.C_NAME, Qt.UserRole)
|
pHandle = trItemP.data(self.C_DATA, self.D_HANDLE)
|
||||||
nwItemS.setParent(pHandle)
|
nwItemS.setParent(pHandle)
|
||||||
trItemP.setExpanded(True)
|
trItemP.setExpanded(True)
|
||||||
logger.debug("The parent of item '%s' has been changed to '%s'", tHandle, pHandle)
|
logger.debug("The parent of item '%s' has been changed to '%s'", tHandle, pHandle)
|
||||||
@@ -1365,7 +1404,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
tItem = self._getTreeItem(tHandle)
|
tItem = self._getTreeItem(tHandle)
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
return 0
|
return 0
|
||||||
return int(tItem.data(self.C_COUNT, Qt.UserRole))
|
return int(tItem.data(self.C_DATA, self.D_WORDS))
|
||||||
|
|
||||||
def _getTreeItem(self, tHandle):
|
def _getTreeItem(self, tHandle):
|
||||||
"""Return the QTreeWidgetItem of a given item handle.
|
"""Return the QTreeWidgetItem of a given item handle.
|
||||||
@@ -1573,6 +1612,9 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
self.tr("Could not write document content."), docSplit.getError()
|
self.tr("Could not write document content."), docSplit.getError()
|
||||||
], nwAlert.ERROR)
|
], nwAlert.ERROR)
|
||||||
|
|
||||||
|
if splitData.get("moveToTrash", False):
|
||||||
|
self.moveItemToTrash(tHandle, askFirst=False, flush=True)
|
||||||
|
|
||||||
self.saveTreeOrder()
|
self.saveTreeOrder()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -1585,7 +1627,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
"""This is a recursive function returning all items in a tree
|
"""This is a recursive function returning all items in a tree
|
||||||
starting at a given QTreeWidgetItem.
|
starting at a given QTreeWidgetItem.
|
||||||
"""
|
"""
|
||||||
tHandle = tItem.data(self.C_NAME, Qt.UserRole)
|
tHandle = tItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
cCount = tItem.childCount()
|
cCount = tItem.childCount()
|
||||||
|
|
||||||
# Update tree-related meta data
|
# Update tree-related meta data
|
||||||
@@ -1618,8 +1660,8 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
newItem.setTextAlignment(self.C_ACTIVE, Qt.AlignLeft)
|
newItem.setTextAlignment(self.C_ACTIVE, Qt.AlignLeft)
|
||||||
newItem.setTextAlignment(self.C_STATUS, Qt.AlignLeft)
|
newItem.setTextAlignment(self.C_STATUS, Qt.AlignLeft)
|
||||||
|
|
||||||
newItem.setData(self.C_NAME, Qt.UserRole, tHandle)
|
newItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
|
||||||
newItem.setData(self.C_COUNT, Qt.UserRole, 0)
|
newItem.setData(self.C_DATA, self.D_WORDS, 0)
|
||||||
|
|
||||||
self._treeMap[tHandle] = newItem
|
self._treeMap[tHandle] = newItem
|
||||||
if pHandle is None:
|
if pHandle is None:
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ class GuiMainStatus(QStatusBar):
|
|||||||
# The Spell Checker Language
|
# The Spell Checker Language
|
||||||
self.langIcon = QLabel("")
|
self.langIcon = QLabel("")
|
||||||
self.langText = QLabel(self.tr("None"))
|
self.langText = QLabel(self.tr("None"))
|
||||||
self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
|
|
||||||
self.langIcon.setContentsMargins(0, 0, 0, 0)
|
self.langIcon.setContentsMargins(0, 0, 0, 0)
|
||||||
self.langText.setContentsMargins(0, 0, xM, 0)
|
self.langText.setContentsMargins(0, 0, xM, 0)
|
||||||
self.addPermanentWidget(self.langIcon)
|
self.addPermanentWidget(self.langIcon)
|
||||||
@@ -91,7 +90,6 @@ class GuiMainStatus(QStatusBar):
|
|||||||
# The Project and Session Stats
|
# The Project and Session Stats
|
||||||
self.statsIcon = QLabel()
|
self.statsIcon = QLabel()
|
||||||
self.statsText = QLabel("")
|
self.statsText = QLabel("")
|
||||||
self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
|
|
||||||
self.statsIcon.setContentsMargins(0, 0, 0, 0)
|
self.statsIcon.setContentsMargins(0, 0, 0, 0)
|
||||||
self.statsText.setContentsMargins(0, 0, xM, 0)
|
self.statsText.setContentsMargins(0, 0, xM, 0)
|
||||||
self.addPermanentWidget(self.statsIcon)
|
self.addPermanentWidget(self.statsIcon)
|
||||||
@@ -99,12 +97,8 @@ class GuiMainStatus(QStatusBar):
|
|||||||
|
|
||||||
# The Session Clock
|
# The Session Clock
|
||||||
# Set the mimimum width so the label doesn't rescale every second
|
# Set the mimimum width so the label doesn't rescale every second
|
||||||
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
|
|
||||||
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
|
|
||||||
|
|
||||||
self.timeIcon = QLabel()
|
self.timeIcon = QLabel()
|
||||||
self.timeText = QLabel("")
|
self.timeText = QLabel("")
|
||||||
self.timeIcon.setPixmap(self.timePixmap)
|
|
||||||
self.timeText.setToolTip(self.tr("Session Time"))
|
self.timeText.setToolTip(self.tr("Session Time"))
|
||||||
self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:"))
|
self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:"))
|
||||||
self.timeIcon.setContentsMargins(0, 0, 0, 0)
|
self.timeIcon.setContentsMargins(0, 0, 0, 0)
|
||||||
@@ -117,6 +111,7 @@ class GuiMainStatus(QStatusBar):
|
|||||||
|
|
||||||
logger.debug("GuiMainStatus initialisation complete")
|
logger.debug("GuiMainStatus initialisation complete")
|
||||||
|
|
||||||
|
self.updateTheme()
|
||||||
self.clearStatus()
|
self.clearStatus()
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -132,6 +127,21 @@ class GuiMainStatus(QStatusBar):
|
|||||||
self.updateTime()
|
self.updateTime()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Update theme elements.
|
||||||
|
"""
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
|
||||||
|
self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
|
||||||
|
self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
|
||||||
|
|
||||||
|
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
|
||||||
|
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
|
||||||
|
|
||||||
|
self.timeIcon.setPixmap(self.timePixmap)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from PyQt5.QtGui import (
|
|||||||
|
|
||||||
from novelwriter.enum import nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemLayout, nwItemType
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import NWConfigParser, minmax, readTextFile
|
from novelwriter.common import NWConfigParser, minmax
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -67,6 +67,7 @@ class GuiTheme:
|
|||||||
self.themeUrl = ""
|
self.themeUrl = ""
|
||||||
self.themeLicense = ""
|
self.themeLicense = ""
|
||||||
self.themeLicenseUrl = ""
|
self.themeLicenseUrl = ""
|
||||||
|
self.themeIcons = ""
|
||||||
|
|
||||||
# GUI
|
# GUI
|
||||||
self.statNone = [120, 120, 120]
|
self.statNone = [120, 120, 120]
|
||||||
@@ -104,16 +105,14 @@ class GuiTheme:
|
|||||||
self.colRepTag = [0, 0, 0]
|
self.colRepTag = [0, 0, 0]
|
||||||
self.colMod = [0, 0, 0]
|
self.colMod = [0, 0, 0]
|
||||||
|
|
||||||
# Changeable Settings
|
|
||||||
self.guiTheme = None
|
|
||||||
self.guiSyntax = None
|
|
||||||
self.syntaxFile = None
|
|
||||||
self.cssFile = None
|
|
||||||
self.guiFontDB = QFontDatabase()
|
|
||||||
|
|
||||||
# Class Setup
|
# Class Setup
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
|
# Init GUI Font
|
||||||
|
self.guiFontDB = QFontDatabase()
|
||||||
|
self._setGuiFont()
|
||||||
|
|
||||||
|
# Load Themes
|
||||||
self._guiPalette = QPalette()
|
self._guiPalette = QPalette()
|
||||||
self._themeList = []
|
self._themeList = []
|
||||||
self._syntaxList = []
|
self._syntaxList = []
|
||||||
@@ -127,9 +126,8 @@ class GuiTheme:
|
|||||||
self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax"))
|
self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax"))
|
||||||
self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes"))
|
self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes"))
|
||||||
|
|
||||||
self.updateFont()
|
self.loadTheme()
|
||||||
self.updateTheme()
|
self.loadSyntax()
|
||||||
self.iconCache.updateTheme()
|
|
||||||
|
|
||||||
# Icon Functions
|
# Icon Functions
|
||||||
self.getIcon = self.iconCache.getIcon
|
self.getIcon = self.iconCache.getIcon
|
||||||
@@ -183,77 +181,26 @@ class GuiTheme:
|
|||||||
return int(ceil(qMetrics.boundingRect(theText).width()))
|
return int(ceil(qMetrics.boundingRect(theText).width()))
|
||||||
|
|
||||||
##
|
##
|
||||||
# Actions
|
# Theme Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateFont(self):
|
|
||||||
"""Update the GUI's font style from settings.
|
|
||||||
"""
|
|
||||||
theFont = QFont()
|
|
||||||
if self.mainConf.guiFont not in self.guiFontDB.families():
|
|
||||||
if self.mainConf.osWindows and "Arial" in self.guiFontDB.families():
|
|
||||||
# On Windows we default to Arial if possible
|
|
||||||
theFont.setFamily("Arial")
|
|
||||||
theFont.setPointSize(10)
|
|
||||||
else:
|
|
||||||
theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
|
|
||||||
self.mainConf.guiFont = theFont.family()
|
|
||||||
self.mainConf.guiFontSize = theFont.pointSize()
|
|
||||||
else:
|
|
||||||
theFont.setFamily(self.mainConf.guiFont)
|
|
||||||
theFont.setPointSize(self.mainConf.guiFontSize)
|
|
||||||
|
|
||||||
qApp.setFont(theFont)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def updateTheme(self):
|
|
||||||
"""Update the GUI theme from theme files.
|
|
||||||
"""
|
|
||||||
self.guiTheme = self.mainConf.guiTheme
|
|
||||||
self.guiSyntax = self.mainConf.guiSyntax
|
|
||||||
|
|
||||||
self.themeFile = self._availThemes.get(self.guiTheme, None)
|
|
||||||
if self.themeFile is None:
|
|
||||||
logger.error("Could not find GUI theme '%s'", self.guiTheme)
|
|
||||||
else:
|
|
||||||
self.cssFile = self.themeFile[:-5]+".css"
|
|
||||||
self.loadTheme()
|
|
||||||
|
|
||||||
self.syntaxFile = self._availSyntax.get(self.guiSyntax, None)
|
|
||||||
if self.syntaxFile is None:
|
|
||||||
logger.error("Could not find syntax theme '%s'", self.guiSyntax)
|
|
||||||
else:
|
|
||||||
self.loadSyntax()
|
|
||||||
|
|
||||||
# Update dependant colours
|
|
||||||
backCol = qApp.palette().window().color()
|
|
||||||
textCol = qApp.palette().windowText().color()
|
|
||||||
|
|
||||||
backLCol = backCol.lightnessF()
|
|
||||||
textLCol = textCol.lightnessF()
|
|
||||||
|
|
||||||
if backLCol > textLCol:
|
|
||||||
helpLCol = textLCol + 0.65*(backLCol - textLCol)
|
|
||||||
else:
|
|
||||||
helpLCol = backLCol + 0.65*(textLCol - backLCol)
|
|
||||||
|
|
||||||
self.helpText = [int(255*helpLCol)]*3
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def loadTheme(self):
|
def loadTheme(self):
|
||||||
"""Load the currently specified GUI theme.
|
"""Load the currently specified GUI theme.
|
||||||
"""
|
"""
|
||||||
logger.info("Loading GUI theme '%s'", self.guiTheme)
|
guiTheme = self.mainConf.guiTheme
|
||||||
|
themeFile = self._availThemes.get(guiTheme, None)
|
||||||
|
if themeFile is None:
|
||||||
|
logger.error("Could not find GUI theme '%s'", guiTheme)
|
||||||
|
return False
|
||||||
|
|
||||||
# Config File
|
# Config File
|
||||||
|
logger.info("Loading GUI theme '%s'", guiTheme)
|
||||||
confParser = NWConfigParser()
|
confParser = NWConfigParser()
|
||||||
try:
|
try:
|
||||||
with open(self.themeFile, mode="r", encoding="utf-8") as inFile:
|
with open(themeFile, mode="r", encoding="utf-8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load theme settings from: %s", self.themeFile)
|
logger.error("Could not load theme settings from: %s", themeFile)
|
||||||
logException()
|
logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -267,6 +214,7 @@ class GuiTheme:
|
|||||||
self.themeUrl = confParser.rdStr(cnfSec, "url", "")
|
self.themeUrl = confParser.rdStr(cnfSec, "url", "")
|
||||||
self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
|
self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
|
||||||
self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
||||||
|
self.themeIcons = confParser.rdStr(cnfSec, "icontheme", "")
|
||||||
|
|
||||||
# Palette
|
# Palette
|
||||||
cnfSec = "Palette"
|
cnfSec = "Palette"
|
||||||
@@ -285,18 +233,32 @@ class GuiTheme:
|
|||||||
self._setPalette(confParser, cnfSec, "highlightedtext", QPalette.HighlightedText)
|
self._setPalette(confParser, cnfSec, "highlightedtext", QPalette.HighlightedText)
|
||||||
self._setPalette(confParser, cnfSec, "link", QPalette.Link)
|
self._setPalette(confParser, cnfSec, "link", QPalette.Link)
|
||||||
self._setPalette(confParser, cnfSec, "linkvisited", QPalette.LinkVisited)
|
self._setPalette(confParser, cnfSec, "linkvisited", QPalette.LinkVisited)
|
||||||
|
else:
|
||||||
|
self._guiPalette = qApp.style().standardPalette()
|
||||||
|
|
||||||
# GUI
|
# GUI
|
||||||
cnfSec = "GUI"
|
cnfSec = "GUI"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
|
self.statNone = self._parseColour(confParser, cnfSec, "statusnone")
|
||||||
self.statUnsaved = self._loadColour(confParser, cnfSec, "statusunsaved")
|
self.statUnsaved = self._parseColour(confParser, cnfSec, "statusunsaved")
|
||||||
self.statSaved = self._loadColour(confParser, cnfSec, "statussaved")
|
self.statSaved = self._parseColour(confParser, cnfSec, "statussaved")
|
||||||
|
|
||||||
# CSS File
|
# Icons
|
||||||
cssData = readTextFile(self.cssFile)
|
self.iconCache.loadTheme(self.themeIcons)
|
||||||
if cssData:
|
|
||||||
qApp.setStyleSheet(cssData)
|
# Update Dependant Colours
|
||||||
|
backCol = self._guiPalette.window().color()
|
||||||
|
textCol = self._guiPalette.windowText().color()
|
||||||
|
|
||||||
|
backLCol = backCol.lightnessF()
|
||||||
|
textLCol = textCol.lightnessF()
|
||||||
|
|
||||||
|
if backLCol > textLCol:
|
||||||
|
helpLCol = textLCol + 0.65*(backLCol - textLCol)
|
||||||
|
else:
|
||||||
|
helpLCol = backLCol + 0.65*(textLCol - backLCol)
|
||||||
|
|
||||||
|
self.helpText = [int(255*helpLCol)]*3
|
||||||
|
|
||||||
# Apply Styles
|
# Apply Styles
|
||||||
qApp.setPalette(self._guiPalette)
|
qApp.setPalette(self._guiPalette)
|
||||||
@@ -306,14 +268,20 @@ class GuiTheme:
|
|||||||
def loadSyntax(self):
|
def loadSyntax(self):
|
||||||
"""Load the currently specified syntax highlighter theme.
|
"""Load the currently specified syntax highlighter theme.
|
||||||
"""
|
"""
|
||||||
logger.info("Loading syntax theme '%s'", self.guiSyntax)
|
guiSyntax = self.mainConf.guiSyntax
|
||||||
|
syntaxFile = self._availSyntax.get(guiSyntax, None)
|
||||||
|
if syntaxFile is None:
|
||||||
|
logger.error("Could not find syntax theme '%s'", guiSyntax)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info("Loading syntax theme '%s'", guiSyntax)
|
||||||
|
|
||||||
confParser = NWConfigParser()
|
confParser = NWConfigParser()
|
||||||
try:
|
try:
|
||||||
with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile:
|
with open(syntaxFile, mode="r", encoding="utf-8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load syntax colours from: %s", self.syntaxFile)
|
logger.error("Could not load syntax colours from: %s", syntaxFile)
|
||||||
logException()
|
logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -321,32 +289,32 @@ class GuiTheme:
|
|||||||
cnfSec = "Main"
|
cnfSec = "Main"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.syntaxName = confParser.rdStr(cnfSec, "name", "")
|
self.syntaxName = confParser.rdStr(cnfSec, "name", "")
|
||||||
self.syntaxDescription = confParser.rdStr(cnfSec, "description", "")
|
self.syntaxDescription = confParser.rdStr(cnfSec, "description", "N/A")
|
||||||
self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "")
|
self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "N/A")
|
||||||
self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "")
|
self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "N/A")
|
||||||
self.syntaxUrl = confParser.rdStr(cnfSec, "url", "")
|
self.syntaxUrl = confParser.rdStr(cnfSec, "url", "")
|
||||||
self.syntaxLicense = confParser.rdStr(cnfSec, "license", "")
|
self.syntaxLicense = confParser.rdStr(cnfSec, "license", "N/A")
|
||||||
self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
|
||||||
|
|
||||||
# Syntax
|
# Syntax
|
||||||
cnfSec = "Syntax"
|
cnfSec = "Syntax"
|
||||||
if confParser.has_section(cnfSec):
|
if confParser.has_section(cnfSec):
|
||||||
self.colBack = self._loadColour(confParser, cnfSec, "background")
|
self.colBack = self._parseColour(confParser, cnfSec, "background")
|
||||||
self.colText = self._loadColour(confParser, cnfSec, "text")
|
self.colText = self._parseColour(confParser, cnfSec, "text")
|
||||||
self.colLink = self._loadColour(confParser, cnfSec, "link")
|
self.colLink = self._parseColour(confParser, cnfSec, "link")
|
||||||
self.colHead = self._loadColour(confParser, cnfSec, "headertext")
|
self.colHead = self._parseColour(confParser, cnfSec, "headertext")
|
||||||
self.colHeadH = self._loadColour(confParser, cnfSec, "headertag")
|
self.colHeadH = self._parseColour(confParser, cnfSec, "headertag")
|
||||||
self.colEmph = self._loadColour(confParser, cnfSec, "emphasis")
|
self.colEmph = self._parseColour(confParser, cnfSec, "emphasis")
|
||||||
self.colDialN = self._loadColour(confParser, cnfSec, "straightquotes")
|
self.colDialN = self._parseColour(confParser, cnfSec, "straightquotes")
|
||||||
self.colDialD = self._loadColour(confParser, cnfSec, "doublequotes")
|
self.colDialD = self._parseColour(confParser, cnfSec, "doublequotes")
|
||||||
self.colDialS = self._loadColour(confParser, cnfSec, "singlequotes")
|
self.colDialS = self._parseColour(confParser, cnfSec, "singlequotes")
|
||||||
self.colHidden = self._loadColour(confParser, cnfSec, "hidden")
|
self.colHidden = self._parseColour(confParser, cnfSec, "hidden")
|
||||||
self.colKey = self._loadColour(confParser, cnfSec, "keyword")
|
self.colKey = self._parseColour(confParser, cnfSec, "keyword")
|
||||||
self.colVal = self._loadColour(confParser, cnfSec, "value")
|
self.colVal = self._parseColour(confParser, cnfSec, "value")
|
||||||
self.colSpell = self._loadColour(confParser, cnfSec, "spellcheckline")
|
self.colSpell = self._parseColour(confParser, cnfSec, "spellcheckline")
|
||||||
self.colError = self._loadColour(confParser, cnfSec, "errorline")
|
self.colError = self._parseColour(confParser, cnfSec, "errorline")
|
||||||
self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag")
|
self.colRepTag = self._parseColour(confParser, cnfSec, "replacetag")
|
||||||
self.colMod = self._loadColour(confParser, cnfSec, "modifier")
|
self.colMod = self._parseColour(confParser, cnfSec, "modifier")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -388,52 +356,64 @@ class GuiTheme:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def _setGuiFont(self):
|
||||||
|
"""Update the GUI's font style from settings.
|
||||||
|
"""
|
||||||
|
theFont = QFont()
|
||||||
|
if self.mainConf.guiFont not in self.guiFontDB.families():
|
||||||
|
if self.mainConf.osWindows and "Arial" in self.guiFontDB.families():
|
||||||
|
# On Windows we default to Arial if possible
|
||||||
|
theFont.setFamily("Arial")
|
||||||
|
theFont.setPointSize(10)
|
||||||
|
else:
|
||||||
|
theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
|
||||||
|
self.mainConf.guiFont = theFont.family()
|
||||||
|
self.mainConf.guiFontSize = theFont.pointSize()
|
||||||
|
else:
|
||||||
|
theFont.setFamily(self.mainConf.guiFont)
|
||||||
|
theFont.setPointSize(self.mainConf.guiFontSize)
|
||||||
|
|
||||||
|
qApp.setFont(theFont)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def _listConf(self, targetDict, checkDir):
|
def _listConf(self, targetDict, checkDir):
|
||||||
"""Scan for syntax and gui themes and populate the dictionary.
|
"""Scan for theme config files and populate the dictionary.
|
||||||
"""
|
"""
|
||||||
if not os.path.isdir(checkDir):
|
if not os.path.isdir(checkDir):
|
||||||
return
|
return False
|
||||||
|
|
||||||
for checkFile in os.listdir(checkDir):
|
for checkFile in os.listdir(checkDir):
|
||||||
confPath = os.path.join(checkDir, checkFile)
|
confPath = os.path.join(checkDir, checkFile)
|
||||||
if os.path.isfile(confPath) and confPath.endswith(".conf"):
|
if os.path.isfile(confPath) and confPath.endswith(".conf"):
|
||||||
targetDict[checkFile[:-5]] = confPath
|
targetDict[checkFile[:-5]] = confPath
|
||||||
|
|
||||||
return
|
return True
|
||||||
|
|
||||||
def _loadColour(self, confParser, cnfSec, cnfName):
|
def _parseColour(self, confParser, cnfSec, cnfName):
|
||||||
"""Load a colour value from a config string.
|
"""Parse a colour value from a config string.
|
||||||
"""
|
"""
|
||||||
if confParser.has_option(cnfSec, cnfName):
|
if confParser.has_option(cnfSec, cnfName):
|
||||||
inData = confParser.get(cnfSec, cnfName).split(",")
|
values = confParser.get(cnfSec, cnfName).split(",")
|
||||||
outData = []
|
result = []
|
||||||
try:
|
try:
|
||||||
outData.append(int(inData[0]))
|
result.append(minmax(int(values[0]), 0, 255))
|
||||||
outData.append(int(inData[1]))
|
result.append(minmax(int(values[1]), 0, 255))
|
||||||
outData.append(int(inData[2]))
|
result.append(minmax(int(values[2]), 0, 255))
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load theme colours for '%s' from config file", cnfName)
|
logger.error("Could not load theme colours for '%s' from config file", cnfName)
|
||||||
outData = [0, 0, 0]
|
result = [0, 0, 0]
|
||||||
else:
|
else:
|
||||||
logger.warning("Could not find theme colours for '%s' in config file", cnfName)
|
logger.warning("Could not find theme colours for '%s' in config file", cnfName)
|
||||||
outData = [0, 0, 0]
|
result = [0, 0, 0]
|
||||||
return outData
|
return result
|
||||||
|
|
||||||
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
|
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
|
||||||
"""Set a palette colour value from a config string.
|
"""Set a palette colour value from a config string.
|
||||||
"""
|
"""
|
||||||
readCol = []
|
self._guiPalette.setColor(
|
||||||
if confParser.has_option(cnfSec, cnfName):
|
paletteVal, QColor(*self._parseColour(confParser, cnfSec, cnfName))
|
||||||
inData = confParser.get(cnfSec, cnfName).split(",")
|
)
|
||||||
try:
|
|
||||||
readCol.append(int(inData[0]))
|
|
||||||
readCol.append(int(inData[1]))
|
|
||||||
readCol.append(int(inData[2]))
|
|
||||||
except Exception:
|
|
||||||
logger.error("Could not load theme colours for '%s' from config file", cnfName)
|
|
||||||
return
|
|
||||||
if len(readCol) == 3:
|
|
||||||
self._guiPalette.setColor(paletteVal, QColor(*readCol))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# End Class GuiTheme
|
# End Class GuiTheme
|
||||||
@@ -468,9 +448,9 @@ class GuiIcons:
|
|||||||
"view_editor", "view_novel", "view_outline",
|
"view_editor", "view_novel", "view_outline",
|
||||||
|
|
||||||
# General Button Icons
|
# General Button Icons
|
||||||
"add", "backward", "bookmark", "check", "close", "cross", "down", "edit", "forward",
|
"add", "backward", "bookmark", "checked", "close", "cross", "down", "edit", "forward",
|
||||||
"maximise", "menu", "minimise", "reference", "refresh", "remove", "search_replace",
|
"maximise", "menu", "minimise", "noncheckable", "reference", "refresh", "remove",
|
||||||
"search", "settings", "up",
|
"search_replace", "search", "settings", "unchecked", "up",
|
||||||
|
|
||||||
# Switches
|
# Switches
|
||||||
"sticky-on", "sticky-off",
|
"sticky-on", "sticky-off",
|
||||||
@@ -492,13 +472,11 @@ class GuiIcons:
|
|||||||
# Storage
|
# Storage
|
||||||
self._qIcons = {}
|
self._qIcons = {}
|
||||||
self._themeMap = {}
|
self._themeMap = {}
|
||||||
self._themeList = []
|
|
||||||
self._headerDec = []
|
self._headerDec = []
|
||||||
self._confName = "icons.conf"
|
self._confName = "icons.conf"
|
||||||
|
|
||||||
# Icon Theme Path
|
# Icon Theme Path
|
||||||
self._iconPath = os.path.join(self.mainConf.assetPath, "icons")
|
self._iconPath = os.path.join(self.mainConf.assetPath, "icons")
|
||||||
self._themePath = os.path.join(self._iconPath, "system")
|
|
||||||
|
|
||||||
# Icon Theme Meta
|
# Icon Theme Meta
|
||||||
self.themeName = ""
|
self.themeName = ""
|
||||||
@@ -515,20 +493,19 @@ class GuiIcons:
|
|||||||
# Actions
|
# Actions
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateTheme(self):
|
def loadTheme(self, iconTheme):
|
||||||
"""Update the theme map. This is more of an init, since many of
|
"""Update the theme map. This is more of an init, since many of
|
||||||
the GUI icons cannot really be replaced without writing specific
|
the GUI icons cannot really be replaced without writing specific
|
||||||
update functions for the classes where they're used.
|
update functions for the classes where they're used.
|
||||||
"""
|
"""
|
||||||
self._themeMap = {}
|
self._themeMap = {}
|
||||||
themePath = self._getThemePath()
|
themePath = os.path.join(self.mainConf.assetPath, "icons", iconTheme)
|
||||||
if themePath is None:
|
if not os.path.isdir(themePath):
|
||||||
logger.warning("No icons loaded")
|
logger.warning("No icons loaded for '%s'", iconTheme)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._themePath = themePath
|
|
||||||
themeConf = os.path.join(themePath, self._confName)
|
themeConf = os.path.join(themePath, self._confName)
|
||||||
logger.info("Loading icon theme '%s'", self.mainConf.guiIcons)
|
logger.info("Loading icon theme '%s'", iconTheme)
|
||||||
|
|
||||||
# Config File
|
# Config File
|
||||||
confParser = NWConfigParser()
|
confParser = NWConfigParser()
|
||||||
@@ -558,7 +535,7 @@ class GuiIcons:
|
|||||||
if iconName not in self.ICON_KEYS:
|
if iconName not in self.ICON_KEYS:
|
||||||
logger.error("Unknown icon name '%s' in config file", iconName)
|
logger.error("Unknown icon name '%s' in config file", iconName)
|
||||||
else:
|
else:
|
||||||
iconPath = os.path.join(self._themePath, iconFile)
|
iconPath = os.path.join(themePath, iconFile)
|
||||||
if os.path.isfile(iconPath):
|
if os.path.isfile(iconPath):
|
||||||
self._themeMap[iconName] = iconPath
|
self._themeMap[iconName] = iconPath
|
||||||
logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile)
|
logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile)
|
||||||
@@ -574,6 +551,14 @@ class GuiIcons:
|
|||||||
if iconKey not in self._themeMap:
|
if iconKey not in self._themeMap:
|
||||||
logger.error("No icon file specified for '%s'", iconKey)
|
logger.error("No icon file specified for '%s'", iconKey)
|
||||||
|
|
||||||
|
# Refresh icons
|
||||||
|
for iconKey in self._qIcons:
|
||||||
|
logger.debug("Reloading icon: '%s'", iconKey)
|
||||||
|
qIcon = self._loadIcon(iconKey)
|
||||||
|
self._qIcons[iconKey] = qIcon
|
||||||
|
|
||||||
|
self._headerDec = []
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -595,7 +580,7 @@ class GuiIcons:
|
|||||||
return QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
if not os.path.isfile(imgPath):
|
if not os.path.isfile(imgPath):
|
||||||
logger.error("Asset '%s' not found", self.IMAGE_MAP[decoKey])
|
logger.error("Asset not found: %s", imgPath)
|
||||||
return QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
theDeco = QPixmap(imgPath)
|
theDeco = QPixmap(imgPath)
|
||||||
@@ -608,7 +593,7 @@ class GuiIcons:
|
|||||||
|
|
||||||
return theDeco
|
return theDeco
|
||||||
|
|
||||||
def getIcon(self, iconKey, iconSize=None):
|
def getIcon(self, iconKey):
|
||||||
"""Return an icon from the icon buffer. If it doesn't exist,
|
"""Return an icon from the icon buffer. If it doesn't exist,
|
||||||
return, load it, and if it still doesn't exist, return an empty
|
return, load it, and if it still doesn't exist, return an empty
|
||||||
icon.
|
icon.
|
||||||
@@ -668,49 +653,10 @@ class GuiIcons:
|
|||||||
]
|
]
|
||||||
return self._headerDec[minmax(hLevel, 0, 4)]
|
return self._headerDec[minmax(hLevel, 0, 4)]
|
||||||
|
|
||||||
def listThemes(self):
|
|
||||||
"""Scan the icons themes folder and list all themes.
|
|
||||||
"""
|
|
||||||
if self._themeList:
|
|
||||||
return self._themeList
|
|
||||||
|
|
||||||
confParser = NWConfigParser()
|
|
||||||
for themeDir in os.listdir(self._iconPath):
|
|
||||||
themePath = os.path.join(self._iconPath, themeDir)
|
|
||||||
if not os.path.isdir(themePath):
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.debug("Checking icon theme config for '%s'", themeDir)
|
|
||||||
themeConf = os.path.join(themePath, self._confName)
|
|
||||||
themeName = _loadInternalName(confParser, themeConf)
|
|
||||||
if themeName:
|
|
||||||
self._themeList.append((themeDir, themeName))
|
|
||||||
|
|
||||||
self._themeList = sorted(self._themeList, key=lambda x: x[1])
|
|
||||||
|
|
||||||
return self._themeList
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _getThemePath(self):
|
|
||||||
"""Get a valid theme path. Returns None if it fails.
|
|
||||||
"""
|
|
||||||
themePath = os.path.join(self.mainConf.assetPath, "icons", self.mainConf.guiIcons)
|
|
||||||
if not os.path.isdir(themePath):
|
|
||||||
logger.warning(
|
|
||||||
"Icon theme '%s' not found, resetting to default", self.mainConf.guiIcons
|
|
||||||
)
|
|
||||||
self.mainConf.setDefaultIconTheme()
|
|
||||||
|
|
||||||
themePath = os.path.join(self.mainConf.assetPath, "icons", self.mainConf.guiIcons)
|
|
||||||
if not os.path.isdir(themePath):
|
|
||||||
logger.error("Default icon theme not found")
|
|
||||||
return None
|
|
||||||
|
|
||||||
return themePath
|
|
||||||
|
|
||||||
def _loadIcon(self, iconKey):
|
def _loadIcon(self, iconKey):
|
||||||
"""Load an icon from the assets themes folder. Is guaranteed to
|
"""Load an icon from the assets themes folder. Is guaranteed to
|
||||||
return a QIcon.
|
return a QIcon.
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ class GuiViewsBar(QToolBar):
|
|||||||
self.setIconSize(QSize(iPx, iPx))
|
self.setIconSize(QSize(iPx, iPx))
|
||||||
self.setMaximumWidth(mPx)
|
self.setMaximumWidth(mPx)
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setStyleSheet("QToolBar {border: 0px;}")
|
|
||||||
|
|
||||||
stretch = QWidget(self)
|
stretch = QWidget(self)
|
||||||
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
@@ -70,37 +69,31 @@ class GuiViewsBar(QToolBar):
|
|||||||
self.aProject = QAction(self.tr("Project"), self)
|
self.aProject = QAction(self.tr("Project"), self)
|
||||||
self.aProject.setFont(lblFont)
|
self.aProject.setFont(lblFont)
|
||||||
self.aProject.setToolTip(self.tr("Project Tree View"))
|
self.aProject.setToolTip(self.tr("Project Tree View"))
|
||||||
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
|
|
||||||
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
|
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
|
||||||
|
|
||||||
self.aNovel = QAction(self.tr("Novel"), self)
|
self.aNovel = QAction(self.tr("Novel"), self)
|
||||||
self.aNovel.setFont(lblFont)
|
self.aNovel.setFont(lblFont)
|
||||||
self.aNovel.setToolTip(self.tr("Novel Tree View"))
|
self.aNovel.setToolTip(self.tr("Novel Tree View"))
|
||||||
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
|
|
||||||
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
|
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
|
||||||
|
|
||||||
self.aOutline = QAction(self.tr("Outline"), self)
|
self.aOutline = QAction(self.tr("Outline"), self)
|
||||||
self.aOutline.setFont(lblFont)
|
self.aOutline.setFont(lblFont)
|
||||||
self.aOutline.setToolTip(self.tr("Novel Outline View"))
|
self.aOutline.setToolTip(self.tr("Novel Outline View"))
|
||||||
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
|
|
||||||
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
|
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
|
||||||
|
|
||||||
self.aBuild = QAction(self.tr("Build"), self)
|
self.aBuild = QAction(self.tr("Build"), self)
|
||||||
self.aBuild.setFont(lblFont)
|
self.aBuild.setFont(lblFont)
|
||||||
self.aBuild.setToolTip(self.tr("Build Novel Project"))
|
self.aBuild.setToolTip(self.tr("Build Novel Project"))
|
||||||
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
|
|
||||||
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
||||||
|
|
||||||
self.aDetails = QAction(self.tr("Details"), self)
|
self.aDetails = QAction(self.tr("Details"), self)
|
||||||
self.aDetails.setFont(lblFont)
|
self.aDetails.setFont(lblFont)
|
||||||
self.aDetails.setToolTip(self.tr("Project Details"))
|
self.aDetails.setToolTip(self.tr("Project Details"))
|
||||||
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
|
|
||||||
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
|
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
|
||||||
|
|
||||||
self.aStats = QAction(self.tr("Stats"), self)
|
self.aStats = QAction(self.tr("Stats"), self)
|
||||||
self.aStats.setFont(lblFont)
|
self.aStats.setFont(lblFont)
|
||||||
self.aStats.setToolTip(self.tr("Writing Statistics"))
|
self.aStats.setToolTip(self.tr("Writing Statistics"))
|
||||||
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
|
|
||||||
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
||||||
|
|
||||||
# Settings Menu
|
# Settings Menu
|
||||||
@@ -114,7 +107,6 @@ class GuiViewsBar(QToolBar):
|
|||||||
self.tbSettings = QToolButton(self)
|
self.tbSettings = QToolButton(self)
|
||||||
self.tbSettings.setFont(lblFont)
|
self.tbSettings.setFont(lblFont)
|
||||||
self.tbSettings.setText(self.tr("Settings"))
|
self.tbSettings.setText(self.tr("Settings"))
|
||||||
self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
|
|
||||||
self.tbSettings.setMenu(self.mSettings)
|
self.tbSettings.setMenu(self.mSettings)
|
||||||
self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||||
self.tbSettings.setPopupMode(QToolButton.InstantPopup)
|
self.tbSettings.setPopupMode(QToolButton.InstantPopup)
|
||||||
@@ -129,8 +121,25 @@ class GuiViewsBar(QToolBar):
|
|||||||
self.addAction(self.aStats)
|
self.addAction(self.aStats)
|
||||||
self.addWidget(self.tbSettings)
|
self.addWidget(self.tbSettings)
|
||||||
|
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiViewsBar initialisation complete")
|
logger.debug("GuiViewsBar initialisation complete")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def updateTheme(self):
|
||||||
|
"""Initialise GUI elements that depend on specific settings.
|
||||||
|
"""
|
||||||
|
self.setStyleSheet("QToolBar {border: 0px;}")
|
||||||
|
|
||||||
|
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
|
||||||
|
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
|
||||||
|
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
|
||||||
|
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
|
||||||
|
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
|
||||||
|
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
|
||||||
|
self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# END Class GuiViewsBar
|
# END Class GuiViewsBar
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
|
||||||
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
|
||||||
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
|
||||||
|
self.projView.projectSettingsRequest.connect(self.showProjectSettingsDialog)
|
||||||
|
|
||||||
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
self.novelView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||||
self.novelView.openDocumentRequest.connect(self._openDocument)
|
self.novelView.openDocumentRequest.connect(self._openDocument)
|
||||||
@@ -806,15 +807,11 @@ class GuiMain(QMainWindow):
|
|||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None and (self.docEditor.anyFocus() or self.isFocusMode):
|
||||||
if self.docEditor.anyFocus() or self.isFocusMode:
|
tHandle = self.docEditor.docHandle()
|
||||||
tHandle = self.docEditor.docHandle()
|
self.projView.renameTreeItem(tHandle)
|
||||||
else:
|
|
||||||
tHandle = self.projView.getSelectedHandle()
|
|
||||||
if tHandle:
|
|
||||||
return self.projView.renameTreeItem(tHandle)
|
|
||||||
|
|
||||||
return False
|
return True
|
||||||
|
|
||||||
def rebuildTrees(self):
|
def rebuildTrees(self):
|
||||||
"""Rebuild the project tree.
|
"""Rebuild the project tree.
|
||||||
@@ -910,25 +907,52 @@ class GuiMain(QMainWindow):
|
|||||||
if dlgConf.result() == QDialog.Accepted:
|
if dlgConf.result() == QDialog.Accepted:
|
||||||
logger.debug("Applying new preferences")
|
logger.debug("Applying new preferences")
|
||||||
self.initMain()
|
self.initMain()
|
||||||
self.mainTheme.updateTheme()
|
|
||||||
self.saveDocument()
|
self.saveDocument()
|
||||||
|
|
||||||
|
if dlgConf.needsRestart:
|
||||||
|
self.makeAlert(self.tr(
|
||||||
|
"Some changes will not be applied until novelWriter has been restarted."
|
||||||
|
), nwAlert.INFO)
|
||||||
|
|
||||||
|
if dlgConf.refreshTree:
|
||||||
|
self.projView.populateTree()
|
||||||
|
|
||||||
|
if dlgConf.updateTheme:
|
||||||
|
# We are doing this manually instead of connecting to
|
||||||
|
# qApp.paletteChanged since the processing order matters
|
||||||
|
self.mainTheme.loadTheme()
|
||||||
|
self.docEditor.updateTheme()
|
||||||
|
self.docViewer.updateTheme()
|
||||||
|
self.viewsBar.updateTheme()
|
||||||
|
self.projView.updateTheme()
|
||||||
|
self.novelView.updateTheme()
|
||||||
|
self.outlineView.updateTheme()
|
||||||
|
self.itemDetails.updateTheme()
|
||||||
|
self.mainStatus.updateTheme()
|
||||||
|
|
||||||
|
if dlgConf.updateSyntax:
|
||||||
|
self.mainTheme.loadSyntax()
|
||||||
|
self.docEditor.updateSyntaxColours()
|
||||||
|
|
||||||
self.docEditor.initEditor()
|
self.docEditor.initEditor()
|
||||||
self.docViewer.initViewer()
|
self.docViewer.initViewer()
|
||||||
self.projView.initSettings()
|
self.projView.initSettings()
|
||||||
self.novelView.initSettings()
|
self.novelView.initSettings()
|
||||||
self.outlineView.initSettings()
|
self.outlineView.initSettings()
|
||||||
|
|
||||||
self._updateStatusWordCount()
|
self._updateStatusWordCount()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def showProjectSettingsDialog(self):
|
@pyqtSlot(int)
|
||||||
|
def showProjectSettingsDialog(self, focusTab=GuiProjectSettings.TAB_MAIN):
|
||||||
"""Open the project settings dialog.
|
"""Open the project settings dialog.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
dlgProj = GuiProjectSettings(self)
|
dlgProj = GuiProjectSettings(self, focusTab=focusTab)
|
||||||
dlgProj.exec_()
|
dlgProj.exec_()
|
||||||
|
|
||||||
if dlgProj.result() == QDialog.Accepted:
|
if dlgProj.result() == QDialog.Accepted:
|
||||||
@@ -1316,6 +1340,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.addAction(self.mainMenu.aInsMinus)
|
self.addAction(self.mainMenu.aInsMinus)
|
||||||
self.addAction(self.mainMenu.aInsTimes)
|
self.addAction(self.mainMenu.aInsTimes)
|
||||||
self.addAction(self.mainMenu.aInsDivide)
|
self.addAction(self.mainMenu.aInsDivide)
|
||||||
|
self.addAction(self.mainMenu.aInsSynopsis)
|
||||||
|
|
||||||
for mAction, _ in self.mainMenu.mInsKWItems.values():
|
for mAction, _ in self.mainMenu.mInsKWItems.values():
|
||||||
self.addAction(mAction)
|
self.addAction(mAction)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pyqt5>=5.3
|
pyqt5>=5.10
|
||||||
lxml>=4.2.0
|
lxml>=4.2.0
|
||||||
pyenchant>=3.0.0
|
pyenchant>=3.0.0
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:17:33">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-25 18:45:45">
|
||||||
<project>
|
<project>
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
<saveCount>1385</saveCount>
|
<saveCount>1386</saveCount>
|
||||||
<autoCount>236</autoCount>
|
<autoCount>236</autoCount>
|
||||||
<editTime>69348</editTime>
|
<editTime>69352</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>False</doBackup>
|
<doBackup>False</doBackup>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ classifiers =
|
|||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
Programming Language :: Python :: 3.10
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
||||||
Development Status :: 5 - Production/Stable
|
Development Status :: 5 - Production/Stable
|
||||||
@@ -32,7 +33,7 @@ python_requires = >=3.7
|
|||||||
include_package_data = True
|
include_package_data = True
|
||||||
packages = find:
|
packages = find:
|
||||||
install_requires =
|
install_requires =
|
||||||
pyqt5>=5.3
|
pyqt5>=5.10
|
||||||
lxml>=4.2.0
|
lxml>=4.2.0
|
||||||
pyenchant>=3.0.0
|
pyenchant>=3.0.0
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ def makeCheckSum(sumFile, cwd=None):
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print("Could not generate sha256 file")
|
print("Could not generate sha256 file")
|
||||||
print(str(exc))
|
print(str(exc))
|
||||||
|
return ""
|
||||||
|
|
||||||
return shaFile
|
return shaFile
|
||||||
|
|
||||||
@@ -905,7 +906,6 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False):
|
|||||||
def makeAppImage(sysArgs):
|
def makeAppImage(sysArgs):
|
||||||
"""Build an Appimage
|
"""Build an Appimage
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import argparse
|
import argparse
|
||||||
import platform
|
import platform
|
||||||
@@ -1088,8 +1088,9 @@ def makeAppImage(sysArgs):
|
|||||||
shutil.copyfile("setup/icons/novelwriter.svg", f"{imageDir}/novelwriter.svg")
|
shutil.copyfile("setup/icons/novelwriter.svg", f"{imageDir}/novelwriter.svg")
|
||||||
print("Copied: setup/icons/novelwriter.svg")
|
print("Copied: setup/icons/novelwriter.svg")
|
||||||
|
|
||||||
shutil.copyfile("setup/data/hicolor/256x256/apps/novelwriter.png",
|
shutil.copyfile(
|
||||||
f"{imageDir}/novelwriter.png")
|
"setup/data/hicolor/256x256/apps/novelwriter.png", f"{imageDir}/novelwriter.png"
|
||||||
|
)
|
||||||
print("Copied: setup/data/hicolor/256x256/apps/novelwriter.png")
|
print("Copied: setup/data/hicolor/256x256/apps/novelwriter.png")
|
||||||
|
|
||||||
# Build Appimage
|
# Build Appimage
|
||||||
@@ -1110,7 +1111,9 @@ def makeAppImage(sysArgs):
|
|||||||
print("")
|
print("")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
outFile = glob.glob(f"{bldDir}/*.AppImage")[0]
|
bldFile = glob.glob(f"{bldDir}/*.AppImage")[0]
|
||||||
|
outFile = f"{bldDir}/novelWriter-{pkgVers}-py{pythonVer}-{linuxTag}.AppImage"
|
||||||
|
os.rename(bldFile, outFile)
|
||||||
shaFile = makeCheckSum(os.path.basename(outFile), cwd=bldDir)
|
shaFile = makeCheckSum(os.path.basename(outFile), cwd=bldDir)
|
||||||
|
|
||||||
toUpload(outFile)
|
toUpload(outFile)
|
||||||
@@ -1118,11 +1121,11 @@ def makeAppImage(sysArgs):
|
|||||||
|
|
||||||
return unparsedArgs
|
return unparsedArgs
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Make Windows Setup EXE (build-win-exe)
|
# Make Windows Setup EXE (build-win-exe)
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|
||||||
def makeWindowsEmbedded(sysArgs):
|
def makeWindowsEmbedded(sysArgs):
|
||||||
"""Set up a package with embedded Python and dependencies for
|
"""Set up a package with embedded Python and dependencies for
|
||||||
Windows installation.
|
Windows installation.
|
||||||
|
|||||||
@@ -162,7 +162,11 @@ def mockGUI(monkeypatch, tmpConf):
|
|||||||
def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
|
def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
|
||||||
"""Create an instance of the novelWriter GUI.
|
"""Create an instance of the novelWriter GUI.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Ok)
|
||||||
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
||||||
|
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Ok)
|
||||||
|
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
||||||
|
|
||||||
monkeypatch.setattr("novelwriter.CONFIG", fncConf)
|
monkeypatch.setattr("novelwriter.CONFIG", fncConf)
|
||||||
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
|
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
|
||||||
qtbot.addWidget(nwGUI)
|
qtbot.addWidget(nwGUI)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
[Main]
|
[Main]
|
||||||
timestamp = 2021-12-31 16:45:32
|
timestamp = 2022-10-26 11:19:49
|
||||||
theme = default
|
theme = default
|
||||||
syntax = default_light
|
syntax = default_light
|
||||||
icons = typicons_light
|
|
||||||
guifont =
|
guifont =
|
||||||
guifontsize = 11
|
guifontsize = 11
|
||||||
lastnotes = 0x0
|
lastnotes = 0x0
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
[Main]
|
[Main]
|
||||||
timestamp = 2021-12-31 16:45:34
|
timestamp = 2022-10-26 11:19:51
|
||||||
theme = default
|
theme = default
|
||||||
syntax = default_light
|
syntax = default_light
|
||||||
icons = typicons_light
|
guifont = Cantarell
|
||||||
guifont = Sans
|
|
||||||
guifontsize = 12
|
guifontsize = 12
|
||||||
lastnotes = 0x0
|
lastnotes = 0x0
|
||||||
guilang = en_GB
|
guilang = en_GB
|
||||||
@@ -12,7 +11,7 @@ hidehscroll = True
|
|||||||
|
|
||||||
[Sizes]
|
[Sizes]
|
||||||
geometry = 1200, 650
|
geometry = 1200, 650
|
||||||
preferences = 670, 589
|
preferences = 699, 614
|
||||||
projcols = 200, 60, 140
|
projcols = 200, 60, 140
|
||||||
mainpane = 300, 800
|
mainpane = 300, 800
|
||||||
docpane = 400, 400
|
docpane = 400, 400
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:59">
|
|
||||||
<project>
|
|
||||||
<name>Project Name</name>
|
|
||||||
<title>Project Title</title>
|
|
||||||
<author>Jane Doe</author>
|
|
||||||
<author>John Doh</author>
|
|
||||||
<saveCount>1</saveCount>
|
|
||||||
<autoCount>1</autoCount>
|
|
||||||
<editTime>0</editTime>
|
|
||||||
</project>
|
|
||||||
<settings>
|
|
||||||
<doBackup>True</doBackup>
|
|
||||||
<language>None</language>
|
|
||||||
<spellCheck>False</spellCheck>
|
|
||||||
<spellLang>en</spellLang>
|
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>9</lastWordCount>
|
|
||||||
<novelWordCount>9</novelWordCount>
|
|
||||||
<notesWordCount>0</notesWordCount>
|
|
||||||
<autoReplace>
|
|
||||||
<entry key="A">B</entry>
|
|
||||||
<entry key="C">D</entry>
|
|
||||||
<entry key="This">With This Stuff</entry>
|
|
||||||
</autoReplace>
|
|
||||||
<titleFormat>
|
|
||||||
<title>%title%</title>
|
|
||||||
<chapter>%title%</chapter>
|
|
||||||
<unnumbered>%title%</unnumbered>
|
|
||||||
<scene>* * *</scene>
|
|
||||||
<section></section>
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
|
||||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
|
||||||
<entry key="s000010" count="0" red="20" green="30" blue="40">Final</entry>
|
|
||||||
</status>
|
|
||||||
<importance>
|
|
||||||
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
|
|
||||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
|
||||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
|
||||||
<entry key="i000011" count="0" red="100" green="100" blue="100">Final</entry>
|
|
||||||
</importance>
|
|
||||||
</settings>
|
|
||||||
<content count="8">
|
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
|
||||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">New Chapter</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
|
||||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
|
||||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
|
||||||
</item>
|
|
||||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">Plot</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">Characters</name>
|
|
||||||
</item>
|
|
||||||
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s000000" import="i000004">World</name>
|
|
||||||
</item>
|
|
||||||
</content>
|
|
||||||
</novelWriterXML>
|
|
||||||
@@ -192,32 +192,6 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
|
|||||||
tstConf.doReplaceSQuote = orDoSng
|
tstConf.doReplaceSQuote = orDoSng
|
||||||
assert tstConf.saveConfig() is True
|
assert tstConf.saveConfig() is True
|
||||||
|
|
||||||
# Test Correcting icon theme
|
|
||||||
origIcons = tstConf.guiIcons
|
|
||||||
|
|
||||||
tstConf.guiIcons = "typicons_colour_dark"
|
|
||||||
assert tstConf.saveConfig() is True
|
|
||||||
assert tstConf.loadConfig() is True
|
|
||||||
assert tstConf.guiIcons == "typicons_dark"
|
|
||||||
|
|
||||||
tstConf.guiIcons = "typicons_grey_dark"
|
|
||||||
assert tstConf.saveConfig() is True
|
|
||||||
assert tstConf.loadConfig() is True
|
|
||||||
assert tstConf.guiIcons == "typicons_dark"
|
|
||||||
|
|
||||||
tstConf.guiIcons = "typicons_colour_light"
|
|
||||||
assert tstConf.saveConfig() is True
|
|
||||||
assert tstConf.loadConfig() is True
|
|
||||||
assert tstConf.guiIcons == "typicons_light"
|
|
||||||
|
|
||||||
tstConf.guiIcons = "typicons_grey_light"
|
|
||||||
assert tstConf.saveConfig() is True
|
|
||||||
assert tstConf.loadConfig() is True
|
|
||||||
assert tstConf.guiIcons == "typicons_light"
|
|
||||||
|
|
||||||
tstConf.guiIcons = origIcons
|
|
||||||
assert tstConf.saveConfig()
|
|
||||||
|
|
||||||
# Localisation
|
# Localisation
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,8 @@ def testCoreDocTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, m
|
|||||||
docText = "\n\n".join(docData)
|
docText = "\n\n".join(docData)
|
||||||
docRaw = docText.splitlines()
|
docRaw = docText.splitlines()
|
||||||
assert NWDoc(theProject, hSplitDoc).writeDocument(docText) is True
|
assert NWDoc(theProject, hSplitDoc).writeDocument(docText) is True
|
||||||
|
theProject.tree[hSplitDoc].setStatus(C.sFinished)
|
||||||
|
theProject.tree[hSplitDoc].setImport(C.iMain)
|
||||||
|
|
||||||
docSplitter = DocSplitter(theProject, hSplitDoc)
|
docSplitter = DocSplitter(theProject, hSplitDoc)
|
||||||
assert docSplitter._srcItem.isFileType()
|
assert docSplitter._srcItem.isFileType()
|
||||||
@@ -242,6 +244,11 @@ def testCoreDocTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, m
|
|||||||
"000000000002e", # Scene Five is after Scene Four
|
"000000000002e", # Scene Five is after Scene Four
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Check that status and importance has been preserved
|
||||||
|
for rHandle in resDocHandle:
|
||||||
|
assert theProject.tree[rHandle].itemStatus == C.sFinished
|
||||||
|
assert theProject.tree[rHandle].itemImport == C.iMain
|
||||||
|
|
||||||
# Check handling of improper initialisation
|
# Check handling of improper initialisation
|
||||||
docSplitter = DocSplitter(theProject, C.hInvalid)
|
docSplitter = DocSplitter(theProject, C.hInvalid)
|
||||||
assert docSplitter._srcHandle is None
|
assert docSplitter._srcHandle is None
|
||||||
|
|||||||
@@ -29,12 +29,9 @@ from novelwriter.dialogs.about import GuiAbout
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
|
def testDlgAbout_NWDialog(qtbot, nwGUI):
|
||||||
"""Test the novelWriter about dialogs.
|
"""Test the novelWriter about dialogs.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# NW About
|
# NW About
|
||||||
nwGUI.mainTheme.themeName = "A Theme"
|
nwGUI.mainTheme.themeName = "A Theme"
|
||||||
nwGUI.mainTheme.themeAuthor = "An Author"
|
nwGUI.mainTheme.themeAuthor = "An Author"
|
||||||
@@ -74,8 +71,6 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
|
|||||||
def testDlgAbout_QtDialog(monkeypatch, nwGUI):
|
def testDlgAbout_QtDialog(monkeypatch, nwGUI):
|
||||||
"""Test the Qt about dialogs.
|
"""Test the Qt about dialogs.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *a, **k: None)
|
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *a, **k: None)
|
||||||
|
|
||||||
# Open About
|
# Open About
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PyQt5.QtCore import QItemSelectionModel
|
from PyQt5.QtCore import QItemSelectionModel
|
||||||
from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox
|
from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog
|
||||||
|
|
||||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||||
from novelwriter.dialogs.updates import GuiUpdates
|
from novelwriter.dialogs.updates import GuiUpdates
|
||||||
@@ -30,12 +30,9 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
|
def testDlgOther_QuoteSelect(qtbot, nwGUI):
|
||||||
"""Test the quote symbols dialog.
|
"""Test the quote symbols dialog.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
nwQuot = GuiQuoteSelect(nwGUI)
|
nwQuot = GuiQuoteSelect(nwGUI)
|
||||||
nwQuot.show()
|
nwQuot.show()
|
||||||
|
|
||||||
@@ -52,7 +49,7 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
|
|||||||
assert nwQuot.result() == QDialog.Accepted
|
assert nwQuot.result() == QDialog.Accepted
|
||||||
assert nwQuot.selectedQuote == lastItem
|
assert nwQuot.selectedQuote == lastItem
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
nwQuot._doReject()
|
nwQuot._doReject()
|
||||||
nwQuot.close()
|
nwQuot.close()
|
||||||
|
|
||||||
@@ -63,9 +60,6 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
|
|||||||
def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
|
def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
|
||||||
"""Test the check for updates dialog.
|
"""Test the check for updates dialog.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
nwUpdate = GuiUpdates(nwGUI)
|
nwUpdate = GuiUpdates(nwGUI)
|
||||||
nwUpdate.show()
|
nwUpdate.show()
|
||||||
|
|
||||||
@@ -95,7 +89,7 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
|
|||||||
# Trigger from Menu
|
# Trigger from Menu
|
||||||
nwGUI.mainMenu.aUpdates.activate(QAction.Trigger)
|
nwGUI.mainMenu.aUpdates.activate(QAction.Trigger)
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
nwUpdate._doClose()
|
nwUpdate._doClose()
|
||||||
|
|
||||||
# END Test testDlgOther_Updates
|
# END Test testDlgOther_Updates
|
||||||
|
|||||||
@@ -24,19 +24,14 @@ import pytest
|
|||||||
from tools import buildTestProject, C
|
from tools import buildTestProject, C
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
from novelwriter.dialogs.docmerge import GuiDocMerge
|
from novelwriter.dialogs.docmerge import GuiDocMerge
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testDlgMerge_Main(qtbot, nwGUI, fncProj, mockRnd):
|
||||||
"""Test the merge documents tool.
|
"""Test the merge documents tool.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
|
||||||
|
|
||||||
# Create a new project
|
# Create a new project
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ import pytest
|
|||||||
|
|
||||||
from tools import C, buildTestProject
|
from tools import C, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
from novelwriter.dialogs.docsplit import GuiDocSplit
|
from novelwriter.dialogs.docsplit import GuiDocSplit
|
||||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
|
|
||||||
@@ -33,9 +31,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
|
|||||||
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||||
"""Test the split document tool.
|
"""Test the split document tool.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
# Create a new project
|
# Create a new project
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ from novelwriter.config import Config
|
|||||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||||
from novelwriter.dialogs.preferences import GuiPreferences
|
from novelwriter.dialogs.preferences import GuiPreferences
|
||||||
|
|
||||||
keyDelay = 2
|
KEY_DELAY = 1
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
@@ -63,7 +61,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
|||||||
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
|
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
|
||||||
qtbot.addWidget(nwGUI)
|
qtbot.addWidget(nwGUI)
|
||||||
nwGUI.show()
|
nwGUI.show()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
theConf = nwGUI.mainConf
|
theConf = nwGUI.mainConf
|
||||||
assert theConf.confPath == fncDir
|
assert theConf.confPath == fncDir
|
||||||
@@ -72,30 +69,40 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
|||||||
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
|
||||||
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
|
with monkeypatch.context() as mp:
|
||||||
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
|
mp.setattr(GuiPreferences, "updateTheme", lambda *a: True)
|
||||||
|
mp.setattr(GuiPreferences, "updateSyntax", lambda *a: True)
|
||||||
|
mp.setattr(GuiPreferences, "needsRestart", lambda *a: True)
|
||||||
|
mp.setattr(GuiPreferences, "refreshTree", lambda *a: True)
|
||||||
|
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
|
||||||
|
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
|
||||||
|
|
||||||
nwPrefs = getGuiItem("GuiPreferences")
|
nwPrefs = getGuiItem("GuiPreferences")
|
||||||
assert isinstance(nwPrefs, GuiPreferences)
|
assert isinstance(nwPrefs, GuiPreferences)
|
||||||
nwPrefs.show()
|
nwPrefs.show()
|
||||||
assert nwPrefs.mainConf.confPath == fncDir
|
assert nwPrefs.mainConf.confPath == fncDir
|
||||||
|
|
||||||
|
assert nwPrefs.updateTheme is False
|
||||||
|
assert nwPrefs.updateSyntax is False
|
||||||
|
assert nwPrefs.needsRestart is False
|
||||||
|
assert nwPrefs.refreshTree is False
|
||||||
|
|
||||||
# General Settings
|
# General Settings
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabGeneral = nwPrefs.tabGeneral
|
tabGeneral = nwPrefs.tabGeneral
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabGeneral)
|
nwPrefs._tabBox.setCurrentWidget(tabGeneral)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert tabGeneral.showFullPath.isChecked()
|
assert tabGeneral.showFullPath.isChecked()
|
||||||
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
|
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
|
||||||
assert not tabGeneral.showFullPath.isChecked()
|
assert not tabGeneral.showFullPath.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabGeneral.hideVScroll.isChecked()
|
assert not tabGeneral.hideVScroll.isChecked()
|
||||||
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
|
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
|
||||||
assert tabGeneral.hideVScroll.isChecked()
|
assert tabGeneral.hideVScroll.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabGeneral.hideHScroll.isChecked()
|
assert not tabGeneral.hideHScroll.isChecked()
|
||||||
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
|
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
|
||||||
assert tabGeneral.hideHScroll.isChecked()
|
assert tabGeneral.hideHScroll.isChecked()
|
||||||
@@ -104,21 +111,21 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
|||||||
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
|
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
|
||||||
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
|
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabGeneral.guiFontSize.setValue(12)
|
tabGeneral.guiFontSize.setValue(12)
|
||||||
|
|
||||||
# Projects Settings
|
# Projects Settings
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabProjects = nwPrefs.tabProjects
|
tabProjects = nwPrefs.tabProjects
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabProjects)
|
nwPrefs._tabBox.setCurrentWidget(tabProjects)
|
||||||
tabProjects.backupPath = "no/where"
|
tabProjects.backupPath = "no/where"
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabProjects.backupOnClose.isChecked()
|
assert not tabProjects.backupOnClose.isChecked()
|
||||||
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
|
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
|
||||||
assert tabProjects.backupOnClose.isChecked()
|
assert tabProjects.backupOnClose.isChecked()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# Check Browse button
|
# Check Browse button
|
||||||
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
|
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
|
||||||
@@ -126,104 +133,104 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
|||||||
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir")
|
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir")
|
||||||
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
|
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabProjects.autoSaveDoc.setValue(20)
|
tabProjects.autoSaveDoc.setValue(20)
|
||||||
tabProjects.autoSaveProj.setValue(40)
|
tabProjects.autoSaveProj.setValue(40)
|
||||||
|
|
||||||
# Document Settings
|
# Document Settings
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabDocs = nwPrefs.tabDocs
|
tabDocs = nwPrefs.tabDocs
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabDocs)
|
nwPrefs._tabBox.setCurrentWidget(tabDocs)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
qtbot.mouseClick(tabDocs.fontButton, Qt.LeftButton)
|
qtbot.mouseClick(tabDocs.fontButton, Qt.LeftButton)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabDocs.textSize.setValue(13)
|
tabDocs.textSize.setValue(13)
|
||||||
tabDocs.textWidth.setValue(700)
|
tabDocs.textWidth.setValue(700)
|
||||||
tabDocs.focusWidth.setValue(900)
|
tabDocs.focusWidth.setValue(900)
|
||||||
tabDocs.textMargin.setValue(45)
|
tabDocs.textMargin.setValue(45)
|
||||||
tabDocs.tabWidth.setValue(45)
|
tabDocs.tabWidth.setValue(45)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabDocs.hideFocusFooter.isChecked()
|
assert not tabDocs.hideFocusFooter.isChecked()
|
||||||
qtbot.mouseClick(tabDocs.hideFocusFooter, Qt.LeftButton)
|
qtbot.mouseClick(tabDocs.hideFocusFooter, Qt.LeftButton)
|
||||||
assert tabDocs.hideFocusFooter.isChecked()
|
assert tabDocs.hideFocusFooter.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabDocs.doJustify.isChecked()
|
assert not tabDocs.doJustify.isChecked()
|
||||||
qtbot.mouseClick(tabDocs.doJustify, Qt.LeftButton)
|
qtbot.mouseClick(tabDocs.doJustify, Qt.LeftButton)
|
||||||
assert tabDocs.doJustify.isChecked()
|
assert tabDocs.doJustify.isChecked()
|
||||||
|
|
||||||
# Editor Settings
|
# Editor Settings
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabEditor = nwPrefs.tabEditor
|
tabEditor = nwPrefs.tabEditor
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabEditor)
|
nwPrefs._tabBox.setCurrentWidget(tabEditor)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabEditor.showTabsNSpaces.isChecked()
|
assert not tabEditor.showTabsNSpaces.isChecked()
|
||||||
qtbot.mouseClick(tabEditor.showTabsNSpaces, Qt.LeftButton)
|
qtbot.mouseClick(tabEditor.showTabsNSpaces, Qt.LeftButton)
|
||||||
assert tabEditor.showTabsNSpaces.isChecked()
|
assert tabEditor.showTabsNSpaces.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabEditor.showLineEndings.isChecked()
|
assert not tabEditor.showLineEndings.isChecked()
|
||||||
qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton)
|
qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton)
|
||||||
assert tabEditor.showLineEndings.isChecked()
|
assert tabEditor.showLineEndings.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabEditor.autoScroll.isChecked()
|
assert not tabEditor.autoScroll.isChecked()
|
||||||
qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton)
|
qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton)
|
||||||
assert tabEditor.autoScroll.isChecked()
|
assert tabEditor.autoScroll.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabEditor.scrollPastEnd.setValue(0)
|
tabEditor.scrollPastEnd.setValue(0)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabEditor.bigDocLimit.setValue(500)
|
tabEditor.bigDocLimit.setValue(500)
|
||||||
|
|
||||||
# Syntax Settings
|
# Syntax Settings
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabSyntax = nwPrefs.tabSyntax
|
tabSyntax = nwPrefs.tabSyntax
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabSyntax)
|
nwPrefs._tabBox.setCurrentWidget(tabSyntax)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert tabSyntax.highlightQuotes.isChecked()
|
assert tabSyntax.highlightQuotes.isChecked()
|
||||||
qtbot.mouseClick(tabSyntax.highlightQuotes, Qt.LeftButton)
|
qtbot.mouseClick(tabSyntax.highlightQuotes, Qt.LeftButton)
|
||||||
assert not tabSyntax.highlightQuotes.isChecked()
|
assert not tabSyntax.highlightQuotes.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert tabSyntax.highlightEmph.isChecked()
|
assert tabSyntax.highlightEmph.isChecked()
|
||||||
qtbot.mouseClick(tabSyntax.highlightEmph, Qt.LeftButton)
|
qtbot.mouseClick(tabSyntax.highlightEmph, Qt.LeftButton)
|
||||||
assert not tabSyntax.highlightEmph.isChecked()
|
assert not tabSyntax.highlightEmph.isChecked()
|
||||||
|
|
||||||
# Automation Settings
|
# Automation Settings
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabAuto = nwPrefs.tabAuto
|
tabAuto = nwPrefs.tabAuto
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabAuto)
|
nwPrefs._tabBox.setCurrentWidget(tabAuto)
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert tabAuto.autoSelect.isChecked()
|
assert tabAuto.autoSelect.isChecked()
|
||||||
qtbot.mouseClick(tabAuto.autoSelect, Qt.LeftButton)
|
qtbot.mouseClick(tabAuto.autoSelect, Qt.LeftButton)
|
||||||
assert not tabAuto.autoSelect.isChecked()
|
assert not tabAuto.autoSelect.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert tabAuto.doReplace.isChecked()
|
assert tabAuto.doReplace.isChecked()
|
||||||
qtbot.mouseClick(tabAuto.doReplace, Qt.LeftButton)
|
qtbot.mouseClick(tabAuto.doReplace, Qt.LeftButton)
|
||||||
assert not tabAuto.doReplace.isChecked()
|
assert not tabAuto.doReplace.isChecked()
|
||||||
|
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
assert not tabAuto.doReplaceSQuote.isEnabled()
|
assert not tabAuto.doReplaceSQuote.isEnabled()
|
||||||
assert not tabAuto.doReplaceDQuote.isEnabled()
|
assert not tabAuto.doReplaceDQuote.isEnabled()
|
||||||
assert not tabAuto.doReplaceDash.isEnabled()
|
assert not tabAuto.doReplaceDash.isEnabled()
|
||||||
assert not tabAuto.doReplaceDots.isEnabled()
|
assert not tabAuto.doReplaceDots.isEnabled()
|
||||||
|
|
||||||
# Quotation Style
|
# Quotation Style
|
||||||
qtbot.wait(keyDelay)
|
qtbot.wait(KEY_DELAY)
|
||||||
tabQuote = nwPrefs.tabQuote
|
tabQuote = nwPrefs.tabQuote
|
||||||
nwPrefs._tabBox.setCurrentWidget(tabQuote)
|
nwPrefs._tabBox.setCurrentWidget(tabQuote)
|
||||||
|
|
||||||
monkeypatch.setattr(GuiQuoteSelect, "selectedQuote", "'")
|
monkeypatch.setattr(GuiQuoteSelect, "selectedQuote", "'")
|
||||||
monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *args: QDialog.Accepted)
|
monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *a: QDialog.Accepted)
|
||||||
qtbot.mouseClick(tabQuote.btnDoubleStyleC, Qt.LeftButton)
|
qtbot.mouseClick(tabQuote.btnDoubleStyleC, Qt.LeftButton)
|
||||||
|
|
||||||
# Save and Check Config
|
# Save and Check Config
|
||||||
@@ -249,6 +256,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
|||||||
novelwriter.CONFIG = origConf
|
novelwriter.CONFIG = origConf
|
||||||
nwGUI.closeMain()
|
nwGUI.closeMain()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testDlgPreferences_Main
|
# END Test testDlgPreferences_Main
|
||||||
|
|||||||
@@ -23,25 +23,15 @@ import pytest
|
|||||||
|
|
||||||
from tools import getGuiItem
|
from tools import getGuiItem
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox
|
from PyQt5.QtWidgets import QAction
|
||||||
|
|
||||||
from novelwriter.dialogs.projdetails import GuiProjectDetails
|
from novelwriter.dialogs.projdetails import GuiProjectDetails
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
|
def testDlgProjDetails_Dialog(qtbot, nwGUI, nwLipsum):
|
||||||
"""Test the project details dialog.
|
"""Test the project details dialog.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Create a project to work on
|
# Create a project to work on
|
||||||
assert nwGUI.openProject(nwLipsum)
|
assert nwGUI.openProject(nwLipsum)
|
||||||
assert nwGUI.rebuildIndex(beQuiet=True)
|
assert nwGUI.rebuildIndex(beQuiet=True)
|
||||||
@@ -54,7 +44,6 @@ def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
|
|
||||||
projDet = getGuiItem("GuiProjectDetails")
|
projDet = getGuiItem("GuiProjectDetails")
|
||||||
assert isinstance(projDet, GuiProjectDetails)
|
assert isinstance(projDet, GuiProjectDetails)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Overview Page
|
# Overview Page
|
||||||
# =============
|
# =============
|
||||||
@@ -105,7 +94,7 @@ def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i]
|
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i]
|
||||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i]
|
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i]
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# Clean Up
|
# Clean Up
|
||||||
projDet._doClose()
|
projDet._doClose()
|
||||||
|
|||||||
@@ -26,29 +26,19 @@ from tools import getGuiItem
|
|||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog,
|
QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog
|
||||||
QMessageBox
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.dialogs.projload import GuiProjectLoad
|
from novelwriter.dialogs.projload import GuiProjectLoad
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||||
"""Test the load project wizard.
|
"""Test the load project wizard.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
assert nwGUI.openProject(nwMinimal)
|
assert nwGUI.openProject(nwMinimal)
|
||||||
assert nwGUI.closeProject()
|
assert nwGUI.closeProject()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
|
||||||
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
|
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
|
||||||
@@ -58,22 +48,18 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
assert isinstance(nwLoad, GuiProjectLoad)
|
assert isinstance(nwLoad, GuiProjectLoad)
|
||||||
nwLoad.show()
|
nwLoad.show()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
recentCount = nwLoad.listBox.topLevelItemCount()
|
recentCount = nwLoad.listBox.topLevelItemCount()
|
||||||
assert recentCount > 0
|
assert recentCount > 0
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
selItem = nwLoad.listBox.topLevelItem(0)
|
selItem = nwLoad.listBox.topLevelItem(0)
|
||||||
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
|
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
|
||||||
assert isinstance(selItem, QTreeWidgetItem)
|
assert isinstance(selItem, QTreeWidgetItem)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwLoad.selPath.setText("")
|
nwLoad.selPath.setText("")
|
||||||
nwLoad.listBox.setCurrentItem(selItem)
|
nwLoad.listBox.setCurrentItem(selItem)
|
||||||
nwLoad._doSelectRecent()
|
nwLoad._doSelectRecent()
|
||||||
assert nwLoad.selPath.text() == selPath
|
assert nwLoad.selPath.text() == selPath
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
|
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
|
||||||
assert nwLoad.openPath == selPath
|
assert nwLoad.openPath == selPath
|
||||||
assert nwLoad.openState == nwLoad.OPEN_STATE
|
assert nwLoad.openState == nwLoad.OPEN_STATE
|
||||||
@@ -81,27 +67,22 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
# Just create a new project load from scratch for the rest of the test
|
# Just create a new project load from scratch for the rest of the test
|
||||||
del nwLoad
|
del nwLoad
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
|
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
|
||||||
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
|
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwLoad = getGuiItem("GuiProjectLoad")
|
nwLoad = getGuiItem("GuiProjectLoad")
|
||||||
assert isinstance(nwLoad, GuiProjectLoad)
|
assert isinstance(nwLoad, GuiProjectLoad)
|
||||||
nwLoad.show()
|
nwLoad.show()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
|
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
|
||||||
assert nwLoad.openPath is None
|
assert nwLoad.openPath is None
|
||||||
assert nwLoad.openState == nwLoad.NONE_STATE
|
assert nwLoad.openState == nwLoad.NONE_STATE
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwLoad.show()
|
nwLoad.show()
|
||||||
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
|
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
|
||||||
assert nwLoad.openPath is None
|
assert nwLoad.openPath is None
|
||||||
assert nwLoad.openState == nwLoad.NEW_STATE
|
assert nwLoad.openState == nwLoad.NEW_STATE
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwLoad.show()
|
nwLoad.show()
|
||||||
nwLoad._doDeleteRecent()
|
nwLoad._doDeleteRecent()
|
||||||
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
|
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
|
||||||
@@ -113,6 +94,6 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
assert nwLoad.openState == nwLoad.OPEN_STATE
|
assert nwLoad.openState == nwLoad.OPEN_STATE
|
||||||
|
|
||||||
nwLoad.close()
|
nwLoad.close()
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testDlgLoadProject_Main
|
# END Test testDlgLoadProject_Main
|
||||||
|
|||||||
@@ -19,56 +19,40 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from shutil import copyfile
|
from novelwriter.enum import nwItemType
|
||||||
from tools import cmpFiles, getGuiItem, buildTestProject
|
from tools import C, getGuiItem, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog
|
from PyQt5.QtWidgets import QDialog, QAction, QColorDialog
|
||||||
|
|
||||||
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||||
|
|
||||||
keyDelay = 2
|
KEY_DELAY = 1
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
|
||||||
importKeys = ["i000004", "i000005", "i000006", "i000007"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgProjSettings_Dialog(
|
def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
||||||
qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir, mockRnd
|
"""Test the main dialog class. Saving settings is not tested in this
|
||||||
):
|
test, but are instead tested in the individual tab tests.
|
||||||
"""Test the full project settings dialog.
|
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncProj, "nwProject.nwx")
|
# Block the GUI blocking thread
|
||||||
testFile = os.path.join(outDir, "guiProjSettings_Dialog_nwProject.nwx")
|
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
|
||||||
compFile = os.path.join(refDir, "guiProjSettings_Dialog_nwProject.nwx")
|
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
|
||||||
|
monkeypatch.setattr(GuiProjectSettings, "spellChanged", lambda *a: True)
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Check that we cannot open when there is no project
|
# Check that we cannot open when there is no project
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||||
assert getGuiItem("GuiProjectSettings") is None
|
assert getGuiItem("GuiProjectSettings") is None
|
||||||
|
|
||||||
# Create new project
|
# Pretend we have a project
|
||||||
buildTestProject(nwGUI, fncProj)
|
nwGUI.hasProject = True
|
||||||
nwGUI.mainConf.backupPath = fncDir
|
|
||||||
|
|
||||||
nwGUI.theProject.setSpellLang("en")
|
nwGUI.theProject.setSpellLang("en")
|
||||||
nwGUI.theProject.setBookAuthors("Jane Smith\nJohn Smith")
|
|
||||||
nwGUI.theProject.setAutoReplace({"A": "B", "C": "D"})
|
|
||||||
|
|
||||||
# Get the dialog object
|
# Get the dialog object
|
||||||
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
|
|
||||||
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
|
|
||||||
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
|
||||||
|
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||||
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
||||||
|
|
||||||
@@ -77,83 +61,180 @@ def testDlgProjSettings_Dialog(
|
|||||||
projEdit.show()
|
projEdit.show()
|
||||||
qtbot.addWidget(projEdit)
|
qtbot.addWidget(projEdit)
|
||||||
|
|
||||||
|
# Switch Tabs
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_REPLACE)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabReplace
|
||||||
|
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabImport
|
||||||
|
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_STATUS)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabStatus
|
||||||
|
|
||||||
|
projEdit._focusTab(GuiProjectSettings.TAB_MAIN)
|
||||||
|
assert projEdit._tabBox.currentWidget() == projEdit.tabMain
|
||||||
|
|
||||||
|
# Clean Up
|
||||||
|
projEdit._doClose()
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testDlgProjSettings_Dialog
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
|
"""Test the main tab of the project settings dialog.
|
||||||
|
"""
|
||||||
|
# Mock components
|
||||||
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
|
# Create new project
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
mockRnd.reset()
|
||||||
|
nwGUI.mainConf.backupPath = fncDir
|
||||||
|
|
||||||
|
# Set some values
|
||||||
|
theProject = nwGUI.theProject
|
||||||
|
theProject.setSpellLang("en")
|
||||||
|
theProject.setBookAuthors("Jane Smith\nJohn Smith")
|
||||||
|
theProject.setAutoReplace({"A": "B", "C": "D"})
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
# Settings Tab
|
# Settings Tab
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
assert projEdit.tabMain.editName.text() == "New Project"
|
tabMain = projSettings.tabMain
|
||||||
assert projEdit.tabMain.editTitle.text() == "New Novel"
|
|
||||||
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
|
|
||||||
assert projEdit.tabMain.spellLang.currentData() == "en"
|
|
||||||
assert projEdit.tabMain.doBackup.isChecked() is False
|
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
assert tabMain.editName.text() == "New Project"
|
||||||
projEdit.tabMain.editName.setText("")
|
assert tabMain.editTitle.text() == "New Novel"
|
||||||
|
assert tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
|
||||||
|
assert tabMain.spellLang.currentData() == "en"
|
||||||
|
assert tabMain.doBackup.isChecked() is False
|
||||||
|
|
||||||
|
tabMain.editName.setText("")
|
||||||
for c in "Project Name":
|
for c in "Project Name":
|
||||||
qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editName, c, delay=KEY_DELAY)
|
||||||
projEdit.tabMain.editTitle.setText("")
|
tabMain.editTitle.setText("")
|
||||||
for c in "Project Title":
|
for c in "Project Title":
|
||||||
qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editTitle, c, delay=KEY_DELAY)
|
||||||
|
|
||||||
projEdit.tabMain.editAuthors.clear()
|
tabMain.editAuthors.clear()
|
||||||
for c in "Jane Doe":
|
for c in "Jane Doe":
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editAuthors, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "John Doh":
|
for c in "John Doh":
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay)
|
qtbot.keyClick(tabMain.editAuthors, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
assert tabMain.editName.text() == "Project Name"
|
||||||
assert projEdit.tabMain.editName.text() == "Project Name"
|
assert tabMain.editTitle.text() == "Project Title"
|
||||||
assert projEdit.tabMain.editTitle.text() == "Project Title"
|
assert tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
|
||||||
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
|
assert projSettings.spellChanged is False
|
||||||
|
|
||||||
|
projSettings._doSave()
|
||||||
|
assert theProject.projName == "Project Name"
|
||||||
|
assert theProject.bookTitle == "Project Title"
|
||||||
|
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
projSettings._doClose()
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testDlgProjSettings_Main
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
|
"""Test the status and importance tabs of the project settings
|
||||||
|
dialog.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
|
# Mock components
|
||||||
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
|
# Create new project
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
nwGUI.mainConf.backupPath = fncDir
|
||||||
|
|
||||||
|
# Set some values
|
||||||
|
theProject = nwGUI.theProject
|
||||||
|
theProject.tree[C.hTitlePage].setStatus(C.sFinished)
|
||||||
|
theProject.tree[C.hChapterDoc].setStatus(C.sDraft)
|
||||||
|
theProject.tree[C.hSceneDoc].setStatus(C.sDraft)
|
||||||
|
|
||||||
|
nwGUI.projView.projTree.setSelectedHandle(C.hPlotRoot)
|
||||||
|
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
|
nwGUI.projView.projTree.setSelectedHandle(C.hCharRoot)
|
||||||
|
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
|
nwGUI.projView.projTree.setSelectedHandle(C.hWorldRoot)
|
||||||
|
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
|
|
||||||
|
hPlotNote = "0000000000010"
|
||||||
|
hCharNote = "0000000000011"
|
||||||
|
hWorldNote = "0000000000012"
|
||||||
|
|
||||||
|
theProject.tree[hPlotNote].setImport(C.iMajor)
|
||||||
|
theProject.tree[hCharNote].setImport(C.iMajor)
|
||||||
|
theProject.tree[hWorldNote].setImport(C.iMain)
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_STATUS)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
# Status Tab
|
# Status Tab
|
||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
projEdit._tabBox.setCurrentWidget(projEdit.tabStatus)
|
tabStatus = projSettings.tabStatus
|
||||||
|
|
||||||
assert projEdit.tabStatus.colChanged is False
|
assert tabStatus.colChanged is False
|
||||||
assert projEdit.tabStatus.getNewList() == ([], [])
|
assert tabStatus.getNewList() == ([], [])
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
|
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
# Can't delete the first item (it's in use)
|
# Can't delete the first item (it's in use)
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
|
||||||
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
|
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
|
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
# Can delete the third item
|
# Can delete the second item
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus.listBox.topLevelItem(2).setSelected(True)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(1))
|
||||||
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
|
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 3
|
assert tabStatus.listBox.topLevelItemCount() == 3
|
||||||
|
|
||||||
# Add a new item
|
# Add a new item
|
||||||
monkeypatch.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
with monkeypatch.context() as mp:
|
||||||
qtbot.mouseClick(projEdit.tabStatus.addButton, Qt.LeftButton)
|
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||||
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
|
qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
|
||||||
for n in range(8):
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
|
||||||
qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=typeDelay)
|
for _ in range(8):
|
||||||
for c in "Final":
|
qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(projEdit.tabStatus.editName, c, delay=typeDelay)
|
for c in "Final":
|
||||||
qtbot.mouseClick(projEdit.tabStatus.colButton, Qt.LeftButton)
|
qtbot.keyClick(tabStatus.editName, c, delay=KEY_DELAY)
|
||||||
qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton)
|
qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
|
||||||
assert projEdit.tabStatus.listBox.topLevelItemCount() == 4
|
qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
assert projEdit.tabStatus.colChanged is True
|
assert tabStatus.colChanged is True
|
||||||
assert projEdit.tabStatus.getNewList() == (
|
assert tabStatus.getNewList() == (
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"key": statusKeys[0],
|
"key": C.sNew,
|
||||||
"name": "New",
|
"name": "New",
|
||||||
"cols": (100, 100, 100)
|
"cols": (100, 100, 100)
|
||||||
}, {
|
}, {
|
||||||
"key": statusKeys[1],
|
"key": C.sDraft,
|
||||||
"name": "Note",
|
"name": "Draft",
|
||||||
"cols": (200, 50, 0)
|
"cols": (200, 150, 0)
|
||||||
}, {
|
}, {
|
||||||
"key": statusKeys[3],
|
"key": C.sFinished,
|
||||||
"name": "Finished",
|
"name": "Finished",
|
||||||
"cols": (50, 200, 0)
|
"cols": (50, 200, 0)
|
||||||
}, {
|
}, {
|
||||||
@@ -162,121 +243,198 @@ def testDlgProjSettings_Dialog(
|
|||||||
"cols": (20, 30, 40)
|
"cols": (20, 30, 40)
|
||||||
}
|
}
|
||||||
], [
|
], [
|
||||||
statusKeys[2] # Deleted item
|
C.sNote # Deleted item
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Move items
|
# Move items, none selected -> no change
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus._moveItem(1)
|
tabStatus._moveItem(1)
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
statusKeys[0], statusKeys[1], statusKeys[3], None
|
C.sNew, C.sDraft, C.sFinished, None
|
||||||
]
|
]
|
||||||
|
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
# Move items, first selected, move up -> no change
|
||||||
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus._moveItem(-1)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
tabStatus._moveItem(-1)
|
||||||
statusKeys[0], statusKeys[1], statusKeys[3], None
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
|
C.sNew, C.sDraft, C.sFinished, None
|
||||||
]
|
]
|
||||||
|
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
# Move items, last selected, move up -> allowed
|
||||||
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
|
tabStatus.listBox.clearSelection()
|
||||||
projEdit.tabStatus._moveItem(-1)
|
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
tabStatus._moveItem(-1)
|
||||||
statusKeys[0], statusKeys[1], None, statusKeys[3]
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
|
C.sNew, C.sDraft, None, C.sFinished
|
||||||
]
|
]
|
||||||
projEdit.tabStatus._moveItem(1)
|
|
||||||
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
|
# Move items, same selected, move down -> allowed
|
||||||
statusKeys[0], statusKeys[1], statusKeys[3], None
|
tabStatus._moveItem(1)
|
||||||
|
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||||
|
C.sNew, C.sDraft, C.sFinished, None
|
||||||
]
|
]
|
||||||
|
|
||||||
# Importance Tab
|
# Importance Tab
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
projEdit._tabBox.setCurrentWidget(projEdit.tabImport)
|
tabImport = projSettings.tabImport
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||||
projEdit.tabImport.listBox.topLevelItem(3).setSelected(True)
|
|
||||||
qtbot.mouseClick(projEdit.tabImport.delButton, Qt.LeftButton)
|
# Delete unused entry
|
||||||
qtbot.mouseClick(projEdit.tabImport.addButton, Qt.LeftButton)
|
tabImport.listBox.clearSelection()
|
||||||
projEdit.tabStatus.listBox.clearSelection()
|
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(1))
|
||||||
projEdit.tabImport.listBox.topLevelItem(3).setSelected(True)
|
qtbot.mouseClick(tabImport.delButton, Qt.LeftButton)
|
||||||
for n in range(8):
|
assert tabImport.listBox.topLevelItemCount() == 3
|
||||||
qtbot.keyClick(projEdit.tabImport.editName, Qt.Key_Backspace, delay=typeDelay)
|
|
||||||
for c in "Final":
|
# Add a new entry
|
||||||
qtbot.keyClick(projEdit.tabImport.editName, c, delay=typeDelay)
|
with monkeypatch.context() as mp:
|
||||||
qtbot.mouseClick(projEdit.tabImport.saveButton, Qt.LeftButton)
|
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||||
qtbot.wait(stepDelay)
|
qtbot.mouseClick(tabImport.addButton, Qt.LeftButton)
|
||||||
|
tabImport.listBox.clearSelection()
|
||||||
|
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3))
|
||||||
|
for _ in range(8):
|
||||||
|
qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=KEY_DELAY)
|
||||||
|
for c in "Final":
|
||||||
|
qtbot.keyClick(tabImport.editName, c, delay=KEY_DELAY)
|
||||||
|
qtbot.mouseClick(tabImport.colButton, Qt.LeftButton)
|
||||||
|
qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton)
|
||||||
|
assert tabImport.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
|
assert tabImport.colChanged is True
|
||||||
|
assert tabImport.getNewList() == (
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": C.iNew,
|
||||||
|
"name": "New",
|
||||||
|
"cols": (100, 100, 100)
|
||||||
|
}, {
|
||||||
|
"key": C.iMajor,
|
||||||
|
"name": "Major",
|
||||||
|
"cols": (200, 150, 0)
|
||||||
|
}, {
|
||||||
|
"key": C.iMain,
|
||||||
|
"name": "Main",
|
||||||
|
"cols": (50, 200, 0)
|
||||||
|
}, {
|
||||||
|
"key": None,
|
||||||
|
"name": "Final",
|
||||||
|
"cols": (20, 30, 40)
|
||||||
|
}
|
||||||
|
], [
|
||||||
|
C.iMinor # Deleted item
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check Project
|
||||||
|
projSettings._doSave()
|
||||||
|
|
||||||
|
statusItems = dict(theProject.statusItems.items())
|
||||||
|
assert statusItems[C.sNew]["name"] == "New"
|
||||||
|
assert statusItems[C.sDraft]["name"] == "Draft"
|
||||||
|
assert statusItems[C.sFinished]["name"] == "Finished"
|
||||||
|
assert statusItems["s000013"]["name"] == "Final"
|
||||||
|
|
||||||
|
importItems = dict(theProject.importItems.items())
|
||||||
|
assert importItems[C.iNew]["name"] == "New"
|
||||||
|
assert importItems[C.iMajor]["name"] == "Major"
|
||||||
|
assert importItems[C.iMain]["name"] == "Main"
|
||||||
|
assert importItems["i000014"]["name"] == "Final"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
# qtbot.stop()
|
||||||
|
projSettings._doClose()
|
||||||
|
|
||||||
|
# END Test testDlgProjSettings_StatusImport
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
|
"""Test the auto-replace tab of the project settings dialog.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
|
# Mock components
|
||||||
|
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
|
||||||
|
|
||||||
|
# Create new project
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
nwGUI.mainConf.backupPath = fncDir
|
||||||
|
|
||||||
|
# Set some values
|
||||||
|
theProject = nwGUI.theProject
|
||||||
|
theProject.autoReplace = {
|
||||||
|
"A": "B", "C": "D"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
# Auto-Replace Tab
|
# Auto-Replace Tab
|
||||||
# ================
|
# ================
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
tabReplace = projSettings.tabReplace
|
||||||
projEdit._tabBox.setCurrentWidget(projEdit.tabReplace)
|
|
||||||
|
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
|
assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>"
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(0).text(1) == "B"
|
assert tabReplace.listBox.topLevelItem(0).text(1) == "B"
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
|
assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>"
|
||||||
assert projEdit.tabReplace.listBox.topLevelItem(1).text(1) == "D"
|
assert tabReplace.listBox.topLevelItem(1).text(1) == "D"
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||||
|
|
||||||
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
|
# Nothing to save or delete
|
||||||
projEdit.tabReplace.listBox.topLevelItem(2).setSelected(True)
|
tabReplace.listBox.clearSelection()
|
||||||
projEdit.tabReplace.editKey.setText("")
|
assert tabReplace._saveEntry() is False
|
||||||
|
assert tabReplace._delEntry() is False
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||||
|
|
||||||
|
# Create a new entry
|
||||||
|
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(0) == "<keyword3>"
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(1) == ""
|
||||||
|
|
||||||
|
# Edit the entry
|
||||||
|
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
|
||||||
|
tabReplace.editKey.setText("")
|
||||||
for c in "Th is ":
|
for c in "Th is ":
|
||||||
qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=typeDelay)
|
qtbot.keyClick(tabReplace.editKey, c, delay=KEY_DELAY)
|
||||||
projEdit.tabReplace.editValue.setText("")
|
tabReplace.editValue.setText("")
|
||||||
for c in "With This Stuff ":
|
for c in "With This Stuff ":
|
||||||
qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=typeDelay)
|
qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY)
|
||||||
qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton)
|
qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>"
|
||||||
|
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff "
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
# Create a new entry again
|
||||||
projEdit.tabReplace.listBox.clearSelection()
|
tabReplace.listBox.clearSelection()
|
||||||
assert not projEdit.tabReplace._saveEntry()
|
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||||
assert not projEdit.tabReplace._delEntry()
|
assert tabReplace.listBox.topLevelItemCount() == 4
|
||||||
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
|
|
||||||
|
|
||||||
|
# The list is sorted, so we must find it
|
||||||
newIdx = -1
|
newIdx = -1
|
||||||
for i in range(projEdit.tabReplace.listBox.topLevelItemCount()):
|
for i in range(tabReplace.listBox.topLevelItemCount()):
|
||||||
if projEdit.tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
|
if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>":
|
||||||
newIdx = i
|
newIdx = i
|
||||||
break
|
break
|
||||||
|
|
||||||
assert newIdx >= 0
|
assert newIdx >= 0
|
||||||
newItem = projEdit.tabReplace.listBox.topLevelItem(newIdx)
|
|
||||||
projEdit.tabReplace.listBox.setCurrentItem(newItem)
|
|
||||||
qtbot.mouseClick(projEdit.tabReplace.delButton, Qt.LeftButton)
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Save & Check
|
# Then delete the new item
|
||||||
# ============
|
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(newIdx))
|
||||||
|
qtbot.mouseClick(tabReplace.delButton, Qt.LeftButton)
|
||||||
|
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||||
|
|
||||||
projEdit._doSave()
|
# Check Project
|
||||||
|
projSettings._doSave()
|
||||||
|
assert theProject.autoReplace == {
|
||||||
|
"A": "B", "C": "D", "This": "With This Stuff"
|
||||||
|
}
|
||||||
|
|
||||||
# Open again, and check project settings
|
# Clean up
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
# qtbot.stop()
|
||||||
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
projSettings._doClose()
|
||||||
|
|
||||||
projEdit = getGuiItem("GuiProjectSettings")
|
# END Test testDlgProjSettings_Replace
|
||||||
assert isinstance(projEdit, GuiProjectSettings)
|
|
||||||
|
|
||||||
qtbot.addWidget(projEdit)
|
|
||||||
assert projEdit.tabMain.editName.text() == "Project Name"
|
|
||||||
assert projEdit.tabMain.editTitle.text() == "Project Title"
|
|
||||||
theAuth = projEdit.tabMain.editAuthors.toPlainText().strip().splitlines()
|
|
||||||
assert len(theAuth) == 2
|
|
||||||
assert theAuth[0] == "Jane Doe"
|
|
||||||
assert theAuth[1] == "John Doh"
|
|
||||||
|
|
||||||
projEdit._doClose()
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
assert nwGUI.saveProject()
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Check the files
|
|
||||||
copyfile(projFile, testFile)
|
|
||||||
assert cmpFiles(testFile, compFile, [2, 8, 9, 10])
|
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
|
||||||
|
|
||||||
# END Test testDlgProjSettings_Dialog
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QDialog, QMessageBox, QAction
|
from PyQt5.QtWidgets import QDialog, QAction
|
||||||
|
|
||||||
from tools import writeFile, readFile, getGuiItem
|
from tools import writeFile, readFile, getGuiItem
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
@@ -31,25 +31,17 @@ from mock import causeOSError
|
|||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
from novelwriter.dialogs.wordlist import GuiWordList
|
from novelwriter.dialogs.wordlist import GuiWordList
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||||
"""test the word list editor.
|
"""test the word list editor.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
|
||||||
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
|
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
nwGUI.openProject(nwMinimal)
|
nwGUI.openProject(nwMinimal)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
|
dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
|
||||||
|
|
||||||
# Load the dialog
|
# Load the dialog
|
||||||
@@ -59,7 +51,6 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
wList = getGuiItem("GuiWordList")
|
wList = getGuiItem("GuiWordList")
|
||||||
assert isinstance(wList, GuiWordList)
|
assert isinstance(wList, GuiWordList)
|
||||||
wList.show()
|
wList.show()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# List should be blank
|
# List should be blank
|
||||||
assert wList.listBox.count() == 0
|
assert wList.listBox.count() == 0
|
||||||
@@ -73,7 +64,6 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
"word_f\n"
|
"word_f\n"
|
||||||
"word_b\n"
|
"word_b\n"
|
||||||
))
|
))
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert wList._loadWordList()
|
assert wList._loadWordList()
|
||||||
|
|
||||||
# Check that the content was loaded
|
# Check that the content was loaded
|
||||||
@@ -130,7 +120,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
monkeypatch.setattr("builtins.open", causeOSError)
|
monkeypatch.setattr("builtins.open", causeOSError)
|
||||||
assert not wList._doSave()
|
assert not wList._doSave()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
wList._doClose()
|
wList._doClose()
|
||||||
|
|
||||||
# END Test testDlgWordList_Dialog
|
# END Test testDlgWordList_Dialog
|
||||||
|
|||||||
@@ -25,33 +25,26 @@ from mock import causeOSError
|
|||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox, qApp
|
from PyQt5.QtWidgets import QAction, qApp
|
||||||
|
|
||||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout
|
||||||
from novelwriter.constants import nwKeyWords, nwUnicode
|
from novelwriter.constants import nwKeyWords, nwUnicode
|
||||||
from novelwriter.core.index import countWords
|
from novelwriter.core.index import countWords
|
||||||
from novelwriter.gui.doceditor import GuiDocEditor
|
from novelwriter.gui.doceditor import GuiDocEditor
|
||||||
|
|
||||||
keyDelay = 2
|
KEY_DELAY = 1
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test initialising the editor.
|
"""Test initialising the editor.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
assert nwGUI.openProject(nwMinimal)
|
assert nwGUI.openProject(nwMinimal)
|
||||||
assert nwGUI.openDocument("8c659a11cd429")
|
assert nwGUI.openDocument("8c659a11cd429")
|
||||||
|
|
||||||
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
|
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
|
||||||
assert nwGUI.saveDocument()
|
assert nwGUI.saveDocument()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Check Defaults
|
# Check Defaults
|
||||||
qDoc = nwGUI.docEditor.document()
|
qDoc = nwGUI.docEditor.document()
|
||||||
@@ -80,7 +73,7 @@ def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
assert nwGUI.docEditor.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
assert nwGUI.docEditor.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
|
||||||
assert nwGUI.docEditor._typPadChar == nwUnicode.U_THNBSP
|
assert nwGUI.docEditor._typPadChar == nwUnicode.U_THNBSP
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_Init
|
# END Test testGuiEditor_Init
|
||||||
|
|
||||||
@@ -89,10 +82,6 @@ def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test loading text into the editor.
|
"""Test loading text into the editor.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
@@ -102,7 +91,6 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
nwGUI.docEditor.replaceText(longText)
|
nwGUI.docEditor.replaceText(longText)
|
||||||
assert nwGUI.saveDocument() is True
|
assert nwGUI.saveDocument() is True
|
||||||
assert nwGUI.closeDocument() is True
|
assert nwGUI.closeDocument() is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Load Text
|
# Load Text
|
||||||
# =========
|
# =========
|
||||||
@@ -142,7 +130,7 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
assert nwGUI.docEditor.loadText(sHandle) is True
|
assert nwGUI.docEditor.loadText(sHandle) is True
|
||||||
assert nwGUI.docEditor.toPlainText() == ""
|
assert nwGUI.docEditor.toPlainText() == ""
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_LoadText
|
# END Test testGuiEditor_LoadText
|
||||||
|
|
||||||
@@ -151,15 +139,10 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test saving text from the editor.
|
"""Test saving text from the editor.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Save Text
|
# Save Text
|
||||||
# =========
|
# =========
|
||||||
@@ -193,24 +176,19 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
# Regular save
|
# Regular save
|
||||||
assert nwGUI.docEditor.saveText() is True
|
assert nwGUI.docEditor.saveText() is True
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_SaveText
|
# END Test testGuiEditor_SaveText
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
|
def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
||||||
"""Test extracting various meta data and other values.
|
"""Test extracting various meta data and other values.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Get Text
|
# Get Text
|
||||||
# This should replace line and paragraph separators, but preserve
|
# This should replace line and paragraph separators, but preserve
|
||||||
@@ -253,21 +231,16 @@ def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test the document actions. This is not an extensive test of the
|
"""Test the document actions. This is not an extensive test of the
|
||||||
action features, just that the actions are actually called. The
|
action features, just that the actions are actually called. The
|
||||||
various action features are tested when their respective functions
|
various action features are tested when their respective functions
|
||||||
are tested.
|
are tested.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -487,7 +460,7 @@ def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
# Unknown Action
|
# Unknown Action
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) is False
|
assert nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) is False
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_Actions
|
# END Test testGuiEditor_Actions
|
||||||
|
|
||||||
@@ -496,15 +469,10 @@ def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test the document insert functions.
|
"""Test the document insert functions.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -577,7 +545,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
"\n\n\n", "\n\n@pov: Jane\n@char: John\n\n", 1
|
"\n\n\n", "\n\n@pov: Jane\n@char: John\n\n", 1
|
||||||
)
|
)
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_Insert
|
# END Test testGuiEditor_Insert
|
||||||
|
|
||||||
@@ -586,15 +554,10 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test the text manipulation functions.
|
"""Test the text manipulation functions.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -791,7 +754,7 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
|
|||||||
assert newPara[6] == twoBits[4]
|
assert newPara[6] == twoBits[4]
|
||||||
assert newPara[7] == " ".join(twoBits[5:])
|
assert newPara[7] == " ".join(twoBits[5:])
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_TextManipulation
|
# END Test testGuiEditor_TextManipulation
|
||||||
|
|
||||||
@@ -800,15 +763,10 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
|
|||||||
def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test the block formatting function.
|
"""Test the block formatting function.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -825,10 +783,6 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTex
|
|||||||
mp.setattr(QTextBlock, "isValid", lambda *a, **k: False)
|
mp.setattr(QTextBlock, "isValid", lambda *a, **k: False)
|
||||||
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) is False
|
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) is False
|
||||||
|
|
||||||
# Empty Block
|
|
||||||
assert nwGUI.docEditor.setCursorLine(1) is True
|
|
||||||
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) is False
|
|
||||||
|
|
||||||
# Keyword
|
# Keyword
|
||||||
assert nwGUI.docEditor.replaceText("@pov: Jane\n\n") is True
|
assert nwGUI.docEditor.replaceText("@pov: Jane\n\n") is True
|
||||||
assert nwGUI.docEditor.setCursorPosition(5) is True
|
assert nwGUI.docEditor.setCursorPosition(5) is True
|
||||||
@@ -1115,24 +1069,19 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTex
|
|||||||
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_COM) is True
|
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_COM) is True
|
||||||
assert nwGUI.docEditor.getText() == "#### Title\n\n% The Text\n\n"
|
assert nwGUI.docEditor.getText() == "#### Title\n\n% The Text\n\n"
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_BlockFormatting
|
# END Test testGuiEditor_BlockFormatting
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test the document editor tags functionality.
|
"""Test the document editor tags functionality.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
assert nwGUI.openProject(nwMinimal) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Create Scene
|
# Create Scene
|
||||||
theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n"
|
theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n"
|
||||||
@@ -1144,7 +1093,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
assert nwGUI.openDocument(cHandle) is True
|
assert nwGUI.openDocument(cHandle) is True
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
assert nwGUI.saveDocument() is True
|
assert nwGUI.saveDocument() is True
|
||||||
assert nwGUI.projView.revealNewTreeItem(cHandle)
|
assert nwGUI.projView.projTree.revealNewTreeItem(cHandle)
|
||||||
nwGUI.docEditor.updateTagHighLighting()
|
nwGUI.docEditor.updateTagHighLighting()
|
||||||
|
|
||||||
# Follow Tag
|
# Follow Tag
|
||||||
@@ -1181,7 +1130,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
assert nwGUI.closeDocViewer() is True
|
assert nwGUI.closeDocViewer() is True
|
||||||
assert nwGUI.docViewer._docHandle is None
|
assert nwGUI.docViewer._docHandle is None
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_Tags
|
# END Test testGuiEditor_Tags
|
||||||
|
|
||||||
@@ -1190,10 +1139,6 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||||
"""Test saving text from the editor.
|
"""Test saving text from the editor.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
class MockThreadPool:
|
class MockThreadPool:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -1226,7 +1171,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count
|
nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count
|
||||||
nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count
|
nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(sHandle) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
theText = "\n\n".join(ipsumText)
|
theText = "\n\n".join(ipsumText)
|
||||||
cC, wC, pC = countWords(theText)
|
cC, wC, pC = countWords(theText)
|
||||||
@@ -1249,7 +1193,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
nwGUI.docEditor.wCounterDoc.run()
|
nwGUI.docEditor.wCounterDoc.run()
|
||||||
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
|
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert nwGUI.theProject.tree[sHandle]._charCount == cC
|
assert nwGUI.theProject.tree[sHandle]._charCount == cC
|
||||||
assert nwGUI.theProject.tree[sHandle]._wordCount == wC
|
assert nwGUI.theProject.tree[sHandle]._wordCount == wC
|
||||||
assert nwGUI.theProject.tree[sHandle]._paraCount == pC
|
assert nwGUI.theProject.tree[sHandle]._paraCount == pC
|
||||||
@@ -1258,7 +1201,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
# Select all text
|
# Select all text
|
||||||
assert nwGUI.docEditor.docFooter._docSelection is False
|
assert nwGUI.docEditor.docFooter._docSelection is False
|
||||||
nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
|
nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert nwGUI.docEditor.docFooter._docSelection is True
|
assert nwGUI.docEditor.docFooter._docSelection is True
|
||||||
|
|
||||||
# Run the selection word counter
|
# Run the selection word counter
|
||||||
@@ -1267,10 +1209,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
nwGUI.docEditor.wCounterSel.run()
|
nwGUI.docEditor.wCounterSel.run()
|
||||||
# nwGUI.docEditor._updateSelCounts(cC, wC, pC)
|
# nwGUI.docEditor._updateSelCounts(cC, wC, pC)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected"
|
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected"
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_WordCounters
|
# END Test testGuiEditor_WordCounters
|
||||||
|
|
||||||
@@ -1279,14 +1220,11 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||||
"""Test the document editor search functionality.
|
"""Test the document editor search functionality.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
||||||
|
|
||||||
assert nwGUI.openProject(nwLipsum) is True
|
assert nwGUI.openProject(nwLipsum) is True
|
||||||
assert nwGUI.openDocument("4c4f28287af27") is True
|
assert nwGUI.openDocument("4c4f28287af27") is True
|
||||||
origText = nwGUI.docEditor.getText()
|
origText = nwGUI.docEditor.getText()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Select the Word "est"
|
# Select the Word "est"
|
||||||
nwGUI.docEditor.setCursorPosition(630)
|
nwGUI.docEditor.setCursorPosition(630)
|
||||||
@@ -1301,11 +1239,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
|
|
||||||
# Find next by enter key
|
# Find next by enter key
|
||||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
|
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
|
||||||
qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3
|
assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3
|
||||||
|
|
||||||
# Find next by button
|
# Find next by button
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3
|
assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3
|
||||||
|
|
||||||
# Activate loop search
|
# Activate loop search
|
||||||
@@ -1323,14 +1261,14 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
assert nwGUI.docEditor.setCursorPosition(15)
|
assert nwGUI.docEditor.setCursorPosition(15)
|
||||||
|
|
||||||
# Toggle search again with header button
|
# Toggle search again with header button
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert nwGUI.docEditor.docSearch.setSearchText("")
|
assert nwGUI.docEditor.docSearch.setSearchText("")
|
||||||
assert nwGUI.docEditor.docSearch.isVisible() is True
|
assert nwGUI.docEditor.docSearch.isVisible() is True
|
||||||
|
|
||||||
# Search for non-existing
|
# Search for non-existing
|
||||||
nwGUI.docEditor.setCursorPosition(0)
|
nwGUI.docEditor.setCursorPosition(0)
|
||||||
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
|
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
|
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
|
||||||
|
|
||||||
# Enable RegEx search
|
# Enable RegEx search
|
||||||
@@ -1341,19 +1279,19 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
# Set invalid RegEx
|
# Set invalid RegEx
|
||||||
nwGUI.docEditor.setCursorPosition(0)
|
nwGUI.docEditor.setCursorPosition(0)
|
||||||
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
|
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
|
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
|
||||||
|
|
||||||
# Set dangerous RegEx (issue #1015)
|
# Set dangerous RegEx (issue #1015)
|
||||||
# If this doesn't get caught, the app will hang
|
# If this doesn't get caught, the app will hang
|
||||||
nwGUI.docEditor.setCursorPosition(0)
|
nwGUI.docEditor.setCursorPosition(0)
|
||||||
assert nwGUI.docEditor.docSearch.setSearchText(r".*")
|
assert nwGUI.docEditor.docSearch.setSearchText(r".*")
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert abs(nwGUI.docEditor.getCursorPosition() - 14) < 3
|
assert abs(nwGUI.docEditor.getCursorPosition() - 14) < 3
|
||||||
|
|
||||||
# Set valid RegEx
|
# Set valid RegEx
|
||||||
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
|
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||||
|
|
||||||
# Find next and then prev
|
# Find next and then prev
|
||||||
@@ -1404,7 +1342,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
|
||||||
|
|
||||||
# Replace "sus" with "foo" via replace button
|
# Replace "sus" with "foo" via replace button
|
||||||
qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay)
|
qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=KEY_DELAY)
|
||||||
assert nwGUI.docEditor.getText()[205:213] == "foocipit"
|
assert nwGUI.docEditor.getText()[205:213] == "foocipit"
|
||||||
|
|
||||||
# Revert last two replaces
|
# Revert last two replaces
|
||||||
@@ -1482,7 +1420,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True)
|
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True)
|
||||||
assert nwGUI.docEditor.focusNextPrevChild(True) is True
|
assert nwGUI.docEditor.focusNextPrevChild(True) is True
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_Search
|
# END Test testGuiEditor_Search
|
||||||
|
|
||||||
|
|||||||
@@ -23,25 +23,17 @@ import pytest
|
|||||||
|
|
||||||
from PyQt5.QtCore import Qt, QUrl
|
from PyQt5.QtCore import Qt, QUrl
|
||||||
from PyQt5.QtGui import QTextCursor
|
from PyQt5.QtGui import QTextCursor
|
||||||
from PyQt5.QtWidgets import qApp, QAction, QMessageBox
|
from PyQt5.QtWidgets import qApp, QAction
|
||||||
from mock import causeException
|
from mock import causeException
|
||||||
|
|
||||||
from novelwriter.enum import nwDocAction
|
from novelwriter.enum import nwDocAction
|
||||||
from novelwriter.core.tohtml import ToHtml
|
from novelwriter.core.tohtml import ToHtml
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||||
"""Test the document viewer.
|
"""Test the document viewer.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
assert nwGUI.openProject(nwLipsum)
|
assert nwGUI.openProject(nwLipsum)
|
||||||
|
|
||||||
@@ -184,6 +176,6 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
assert nwGUI.docViewer.loadText("846352075de7d") is False
|
assert nwGUI.docViewer.loadText("846352075de7d") is False
|
||||||
assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview."
|
assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview."
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiViewer_Main
|
# END Test testGuiViewer_Main
|
||||||
|
|||||||
@@ -37,17 +37,13 @@ from novelwriter.gui.projtree import GuiProjectTree
|
|||||||
from novelwriter.gui.doceditor import GuiDocEditor
|
from novelwriter.gui.doceditor import GuiDocEditor
|
||||||
from novelwriter.gui.noveltree import GuiNovelView
|
from novelwriter.gui.noveltree import GuiNovelView
|
||||||
|
|
||||||
keyDelay = 2
|
KEY_DELAY = 1
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
|
def testGuiMain_ProjectBlocker(nwGUI):
|
||||||
"""Test the blocking of features when there's no project open.
|
"""Test the blocking of features when there's no project open.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Test no-project blocking
|
# Test no-project blocking
|
||||||
assert nwGUI.closeProject() is True
|
assert nwGUI.closeProject() is True
|
||||||
assert nwGUI.saveProject() is False
|
assert nwGUI.saveProject() is False
|
||||||
@@ -74,9 +70,6 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
|
|||||||
def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
|
def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
|
||||||
"""Test creating a new project.
|
"""Test creating a new project.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# No data
|
# No data
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(GuiProjectWizard, "exec_", lambda *a: None)
|
mp.setattr(GuiProjectWizard, "exec_", lambda *a: None)
|
||||||
@@ -113,8 +106,6 @@ def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
|
|||||||
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||||
"""Test handling of project tree items based on GUI focus states.
|
"""Test handling of project tree items based on GUI focus states.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|
||||||
sHandle = "000000000000f"
|
sHandle = "000000000000f"
|
||||||
@@ -165,9 +156,6 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd):
|
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd):
|
||||||
"""Test the document editor.
|
"""Test the document editor.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
||||||
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
|
||||||
@@ -195,11 +183,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx")
|
compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx")
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Re-open project
|
# Re-open project
|
||||||
assert nwGUI.openProject(fncProj)
|
assert nwGUI.openProject(fncProj)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Check that we loaded the data
|
# Check that we loaded the data
|
||||||
assert len(nwGUI.theProject.tree) == 8
|
assert len(nwGUI.theProject.tree) == 8
|
||||||
@@ -241,18 +227,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||||
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
for c in "# Jane Doe":
|
for c in "# Jane Doe":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@tag: Jane":
|
for c in "@tag: Jane":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "This is a file about Jane.":
|
for c in "This is a file about Jane.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
# Add a Plot File
|
# Add a Plot File
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
@@ -263,18 +249,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||||
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
for c in "# Main Plot":
|
for c in "# Main Plot":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@tag: MainPlot":
|
for c in "@tag: MainPlot":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "This is a file detailing the main plot.":
|
for c in "This is a file detailing the main plot.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
# Add a World File
|
# Add a World File
|
||||||
nwGUI.switchFocus(nwWidget.TREE)
|
nwGUI.switchFocus(nwWidget.TREE)
|
||||||
@@ -290,18 +276,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||||
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
for c in "# Main Location":
|
for c in "# Main Location":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@tag: Home":
|
for c in "@tag: Home":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "This is a file describing Jane's home.":
|
for c in "This is a file describing Jane's home.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
# Trigger autosaves before making more changes
|
# Trigger autosaves before making more changes
|
||||||
nwGUI._autoSaveDocument()
|
nwGUI._autoSaveDocument()
|
||||||
@@ -317,67 +303,67 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
# Type something into the document
|
# Type something into the document
|
||||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||||
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
for c in "# Novel":
|
for c in "# Novel":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "## Chapter":
|
for c in "## Chapter":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "@pov: Jane":
|
for c in "@pov: Jane":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@plot: MainPlot":
|
for c in "@plot: MainPlot":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "### Scene":
|
for c in "### Scene":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "% How about a comment?":
|
for c in "% How about a comment?":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@pov: Jane":
|
for c in "@pov: Jane":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@plot: MainPlot":
|
for c in "@plot: MainPlot":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
for c in "@location: Home":
|
for c in "@location: Home":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "#### Some Section":
|
for c in "#### Some Section":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "@char: Jane":
|
for c in "@char: Jane":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "This is a paragraph of nonsense text.":
|
for c in "This is a paragraph of nonsense text.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
# Don't allow Shift+Enter to insert a line separator (issue #1150)
|
# Don't allow Shift+Enter to insert a line separator (issue #1150)
|
||||||
for c in "This is another paragraph":
|
for c in "This is another paragraph":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Enter, modifier=Qt.ShiftModifier, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Enter, modifier=Qt.ShiftModifier, delay=KEY_DELAY)
|
||||||
for c in "with a line separator in it.":
|
for c in "with a line separator in it.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
# Auto-Replace
|
# Auto-Replace
|
||||||
# ============
|
# ============
|
||||||
@@ -386,43 +372,43 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
"This is another paragraph of much longer nonsense text. "
|
"This is another paragraph of much longer nonsense text. "
|
||||||
"It is in fact 1 very very NONSENSICAL nonsense text! "
|
"It is in fact 1 very very NONSENSICAL nonsense text! "
|
||||||
):
|
):
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ":
|
for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
for c in "Isn't that nice? ":
|
for c in "Isn't that nice? ":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ":
|
for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
for c in "Ellipsis? Not a problem either ... ":
|
for c in "Ellipsis? Not a problem either ... ":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
for c in "How about three hyphens - -":
|
for c in "How about three hyphens - -":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=KEY_DELAY)
|
||||||
for c in "- for long dash? It works too.":
|
for c in "- for long dash? It works too.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "\"Full line double quoted text.\"":
|
for c in "\"Full line double quoted text.\"":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "'Full line single quoted text.'":
|
for c in "'Full line single quoted text.'":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
# Insert spaces before and after quotes
|
# Insert spaces before and after quotes
|
||||||
nwGUI.mainConf.fmtPadBefore = "\u201d"
|
nwGUI.mainConf.fmtPadBefore = "\u201d"
|
||||||
nwGUI.mainConf.fmtPadAfter = "\u201c"
|
nwGUI.mainConf.fmtPadAfter = "\u201c"
|
||||||
|
|
||||||
for c in "Some \"double quoted text with spaces padded\".":
|
for c in "Some \"double quoted text with spaces padded\".":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
nwGUI.mainConf.fmtPadBefore = ""
|
nwGUI.mainConf.fmtPadBefore = ""
|
||||||
nwGUI.mainConf.fmtPadAfter = ""
|
nwGUI.mainConf.fmtPadAfter = ""
|
||||||
@@ -431,24 +417,24 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
nwGUI.mainConf.fmtPadBefore = ":"
|
nwGUI.mainConf.fmtPadBefore = ":"
|
||||||
|
|
||||||
for c in "@object: NoSpaceAdded":
|
for c in "@object: NoSpaceAdded":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "% synopsis: No space before this colon.":
|
for c in "% synopsis: No space before this colon.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "Add space before this colon: See?":
|
for c in "Add space before this colon: See?":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "But don't add a double space : See?":
|
for c in "But don't add a double space : See?":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
nwGUI.mainConf.fmtPadBefore = ""
|
nwGUI.mainConf.fmtPadBefore = ""
|
||||||
|
|
||||||
@@ -456,55 +442,49 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
# ================
|
# ================
|
||||||
|
|
||||||
for c in "\t\"Tab-indented text\"":
|
for c in "\t\"Tab-indented text\"":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in ">\"Paragraph-indented text\"":
|
for c in ">\"Paragraph-indented text\"":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in ">>\"Right-aligned text\"":
|
for c in ">>\"Right-aligned text\"":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in "\t'Tab-indented text'":
|
for c in "\t'Tab-indented text'":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in ">'Paragraph-indented text'":
|
for c in ">'Paragraph-indented text'":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
for c in ">>'Right-aligned text'":
|
for c in ">>'Right-aligned text'":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.docEditor.wCounterDoc.run()
|
nwGUI.docEditor.wCounterDoc.run()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Save the document
|
# Save the document
|
||||||
assert nwGUI.docEditor.docChanged()
|
assert nwGUI.docEditor.docChanged()
|
||||||
assert nwGUI.saveDocument()
|
assert nwGUI.saveDocument()
|
||||||
assert not nwGUI.docEditor.docChanged()
|
assert not nwGUI.docEditor.docChanged()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.rebuildIndex()
|
nwGUI.rebuildIndex()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Open and view the edited document
|
# Open and view the edited document
|
||||||
nwGUI.switchFocus(nwWidget.VIEWER)
|
nwGUI.switchFocus(nwWidget.VIEWER)
|
||||||
assert nwGUI.openDocument(C.hSceneDoc)
|
assert nwGUI.openDocument(C.hSceneDoc)
|
||||||
assert nwGUI.viewDocument(C.hSceneDoc)
|
assert nwGUI.viewDocument(C.hSceneDoc)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert nwGUI.saveProject()
|
assert nwGUI.saveProject()
|
||||||
assert nwGUI.closeDocViewer()
|
assert nwGUI.closeDocViewer()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Check a Quick Create and Delete
|
# Check a Quick Create and Delete
|
||||||
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
|
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
|
||||||
@@ -553,11 +533,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testGuiMain_FocusFullMode(qtbot, nwGUI, fncProj, mockRnd):
|
||||||
"""Test toggling focus mode in main window.
|
"""Test toggling focus mode in main window.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
assert nwGUI.isFocusMode is False
|
assert nwGUI.isFocusMode is False
|
||||||
|
|
||||||
@@ -596,6 +574,6 @@ def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
nwGUI.toggleFullScreenMode()
|
nwGUI.toggleFullScreenMode()
|
||||||
assert nwGUI.mainConf.isFullScreen is False
|
assert nwGUI.mainConf.isFullScreen is False
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiMain_FocusFullMode
|
# END Test testGuiMain_FocusFullMode
|
||||||
|
|||||||
@@ -32,24 +32,17 @@ from novelwriter.enum import nwDocAction, nwDocInsert
|
|||||||
from novelwriter.constants import nwKeyWords, nwUnicode
|
from novelwriter.constants import nwKeyWords, nwUnicode
|
||||||
from novelwriter.gui.doceditor import GuiDocEditor
|
from novelwriter.gui.doceditor import GuiDocEditor
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||||
"""Test the main menu Edit and Format entries.
|
"""Test the main menu Edit and Format entries.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
|
||||||
|
|
||||||
# Test Document Action with No Project
|
# Test Document Action with No Project
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False
|
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False
|
||||||
|
|
||||||
assert nwGUI.openProject(nwLipsum) is True
|
assert nwGUI.openProject(nwLipsum) is True
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Split By Chapter
|
# Split By Chapter
|
||||||
assert nwGUI.openDocument("4c4f28287af27") is True
|
assert nwGUI.openDocument("4c4f28287af27") is True
|
||||||
@@ -61,57 +54,43 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
||||||
fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo."
|
fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Italic
|
# Italic
|
||||||
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
||||||
fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo."
|
fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Strikethrough
|
# Strikethrough
|
||||||
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
|
||||||
fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo."
|
fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Should get us back to plain
|
# Should get us back to plain
|
||||||
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Double Quotes
|
# Double Quotes
|
||||||
nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger)
|
||||||
fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo."
|
fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Single Quotes
|
# Single Quotes
|
||||||
nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger)
|
||||||
fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo."
|
fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Block Formats
|
# Block Formats
|
||||||
# =============
|
# =============
|
||||||
@@ -121,61 +100,60 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger)
|
||||||
fmtStr = "# Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "# Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Header 2
|
# Header 2
|
||||||
nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger)
|
||||||
fmtStr = "## Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "## Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:89] == fmtStr
|
assert nwGUI.docEditor.getText()[39:89] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Header 3
|
# Header 3
|
||||||
nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger)
|
||||||
fmtStr = "### Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "### Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Header 4
|
# Header 4
|
||||||
nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger)
|
||||||
fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:91] == fmtStr
|
assert nwGUI.docEditor.getText()[39:91] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
# Title Format
|
||||||
|
nwGUI.mainMenu.aFmtTitle.activate(QAction.Trigger)
|
||||||
|
fmtStr = "#! Pellentesque nec erat ut nulla posuere commodo."
|
||||||
|
assert nwGUI.docEditor.getText()[39:89] == fmtStr
|
||||||
|
|
||||||
|
# Unnumbered Chapter
|
||||||
|
nwGUI.mainMenu.aFmtUnNum.activate(QAction.Trigger)
|
||||||
|
fmtStr = "##! Pellentesque nec erat ut nulla posuere commodo."
|
||||||
|
assert nwGUI.docEditor.getText()[39:90] == fmtStr
|
||||||
|
|
||||||
# Clear Format
|
# Clear Format
|
||||||
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Comment On
|
# Comment On
|
||||||
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
|
||||||
fmtStr = "% Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "% Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
assert nwGUI.docEditor.getText()[39:88] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Comment Off
|
# Comment Off
|
||||||
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Check comment with no space before text
|
# Check comment with no space before text
|
||||||
assert nwGUI.docEditor.setCursorPosition(39)
|
assert nwGUI.docEditor.setCursorPosition(39)
|
||||||
assert nwGUI.docEditor.insertText("%")
|
assert nwGUI.docEditor.insertText("%")
|
||||||
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:87] == fmtStr
|
assert nwGUI.docEditor.getText()[39:87] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Undo/Redo
|
# Undo/Redo
|
||||||
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
|
||||||
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
|
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
|
||||||
assert nwGUI.docEditor.getText()[39:87] == fmtStr
|
assert nwGUI.docEditor.getText()[39:87] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger)
|
nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
assert nwGUI.docEditor.getText()[39:86] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Cut, Copy and Paste
|
# Cut, Copy and Paste
|
||||||
assert nwGUI.docEditor.setCursorPosition(39)
|
assert nwGUI.docEditor.setCursorPosition(39)
|
||||||
@@ -240,36 +218,30 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger)
|
||||||
fmtStr = "A single, short paragraph. <<"
|
fmtStr = "A single, short paragraph. <<"
|
||||||
assert nwGUI.docEditor.getText()[:29] == fmtStr
|
assert nwGUI.docEditor.getText()[:29] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Right Align
|
# Right Align
|
||||||
nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger)
|
||||||
fmtStr = ">> A single, short paragraph."
|
fmtStr = ">> A single, short paragraph."
|
||||||
assert nwGUI.docEditor.getText()[:29] == fmtStr
|
assert nwGUI.docEditor.getText()[:29] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Centre Align
|
# Centre Align
|
||||||
nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger)
|
||||||
fmtStr = ">> A single, short paragraph. <<"
|
fmtStr = ">> A single, short paragraph. <<"
|
||||||
assert nwGUI.docEditor.getText()[:32] == fmtStr
|
assert nwGUI.docEditor.getText()[:32] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Left Indent
|
# Left Indent
|
||||||
nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger)
|
||||||
fmtStr = "> A single, short paragraph."
|
fmtStr = "> A single, short paragraph."
|
||||||
assert nwGUI.docEditor.getText()[:28] == fmtStr
|
assert nwGUI.docEditor.getText()[:28] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Right Indent
|
# Right Indent
|
||||||
nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger)
|
||||||
fmtStr = "> A single, short paragraph. <"
|
fmtStr = "> A single, short paragraph. <"
|
||||||
assert nwGUI.docEditor.getText()[:30] == fmtStr
|
assert nwGUI.docEditor.getText()[:30] == fmtStr
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# No Format
|
# No Format
|
||||||
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText()[:30] == cleanText
|
assert nwGUI.docEditor.getText()[:30] == cleanText
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Other Checks
|
# Other Checks
|
||||||
|
|
||||||
@@ -352,10 +324,6 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
assert nwGUI.docEditor.setCursorPosition(17)
|
assert nwGUI.docEditor.setCursorPosition(17)
|
||||||
assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT)
|
assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT)
|
||||||
|
|
||||||
# Cannot Format Empty Line
|
|
||||||
assert nwGUI.docEditor.setCursorPosition(13)
|
|
||||||
assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT)
|
|
||||||
|
|
||||||
# Invalid Action
|
# Invalid Action
|
||||||
assert nwGUI.docEditor.setCursorPosition(30)
|
assert nwGUI.docEditor.setCursorPosition(30)
|
||||||
assert not nwGUI.docEditor._formatBlock(nwDocAction.NO_ACTION)
|
assert not nwGUI.docEditor._formatBlock(nwDocAction.NO_ACTION)
|
||||||
@@ -368,21 +336,17 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
|
"Also text with \"double\" quotes which are \"less tricky\".\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiMenu_EditFormat
|
# END Test testGuiMenu_EditFormat
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
|
def testGuiMenu_ContextMenus(qtbot, nwGUI, nwLipsum):
|
||||||
"""Test the context menus.
|
"""Test the context menus.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
assert nwGUI.openProject(nwLipsum)
|
assert nwGUI.openProject(nwLipsum)
|
||||||
assert nwGUI.openDocument("4c4f28287af27")
|
assert nwGUI.openDocument("4c4f28287af27")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Editor Context Menu
|
# Editor Context Menu
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
theCursor = nwGUI.docEditor.textCursor()
|
||||||
@@ -452,7 +416,7 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
assert nwGUI.docViewer.docHeader.backButton.isEnabled()
|
assert nwGUI.docViewer.docHeader.backButton.isEnabled()
|
||||||
assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled()
|
assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiMenu_ContextMenus
|
# END Test testGuiMenu_ContextMenus
|
||||||
|
|
||||||
@@ -461,10 +425,6 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
||||||
"""Test the Insert menu.
|
"""Test the Insert menu.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|
||||||
assert nwGUI.projView.projTree._getTreeItem(C.hSceneDoc) is not None
|
assert nwGUI.projView.projTree._getTreeItem(C.hSceneDoc) is not None
|
||||||
@@ -482,7 +442,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
assert nwGUI.docEditor.insertText(None) is False
|
assert nwGUI.docEditor.insertText(None) is False
|
||||||
assert nwGUI.docEditor.isEmpty()
|
assert nwGUI.docEditor.isEmpty()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# Check Menu Entries
|
# Check Menu Entries
|
||||||
nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger)
|
nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger)
|
||||||
@@ -577,9 +537,8 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP
|
assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
##
|
# Insert Keywords
|
||||||
# Insert Keywords
|
# ===============
|
||||||
##
|
|
||||||
|
|
||||||
nwGUI.docEditor.setText("Stuff")
|
nwGUI.docEditor.setText("Stuff")
|
||||||
nwGUI.mainMenu.mInsKWItems[nwKeyWords.TAG_KEY][0].activate(QAction.Trigger)
|
nwGUI.mainMenu.mInsKWItems[nwKeyWords.TAG_KEY][0].activate(QAction.Trigger)
|
||||||
@@ -629,9 +588,15 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
|
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
##
|
# Insert Special Comments
|
||||||
# Insert Break or Space
|
# =======================
|
||||||
##
|
|
||||||
|
nwGUI.docEditor.setText("Stuff\n")
|
||||||
|
nwGUI.mainMenu.aInsSynopsis.activate(QAction.Trigger)
|
||||||
|
assert nwGUI.docEditor.getText() == "Stuff\n% Synopsis: \n"
|
||||||
|
|
||||||
|
# Insert Break or Space
|
||||||
|
# =====================
|
||||||
|
|
||||||
nwGUI.docEditor.setText("### Stuff\n")
|
nwGUI.docEditor.setText("### Stuff\n")
|
||||||
nwGUI.mainMenu.aInsNewPage.activate(QAction.Trigger)
|
nwGUI.mainMenu.aInsNewPage.activate(QAction.Trigger)
|
||||||
@@ -647,9 +612,8 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
|
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
##
|
# Insert Text from File
|
||||||
# Insert text from file
|
# =====================
|
||||||
##
|
|
||||||
|
|
||||||
nwGUI.closeDocument()
|
nwGUI.closeDocument()
|
||||||
|
|
||||||
@@ -676,18 +640,17 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
assert nwGUI.docEditor.getText() == "Bar"
|
assert nwGUI.docEditor.getText() == "Bar"
|
||||||
|
|
||||||
# The document isn't empty, so the message box should pop
|
# The document isn't empty, so the message box should pop
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No)
|
with monkeypatch.context() as mp:
|
||||||
assert not nwGUI.importDocument()
|
mp.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No)
|
||||||
assert nwGUI.docEditor.getText() == "Bar"
|
assert not nwGUI.importDocument()
|
||||||
|
assert nwGUI.docEditor.getText() == "Bar"
|
||||||
|
|
||||||
# Finally, accept the replaced text, this time we use the menu entry to trigger it
|
# Finally, accept the replaced text, this time we use the menu entry to trigger it
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.Yes)
|
|
||||||
nwGUI.mainMenu.aImportFile.activate(QAction.Trigger)
|
nwGUI.mainMenu.aImportFile.activate(QAction.Trigger)
|
||||||
assert nwGUI.docEditor.getText() == "Foo"
|
assert nwGUI.docEditor.getText() == "Foo"
|
||||||
|
|
||||||
##
|
# Reveal File Location
|
||||||
# Reveal file location
|
# ====================
|
||||||
##
|
|
||||||
|
|
||||||
theMessage = ""
|
theMessage = ""
|
||||||
|
|
||||||
@@ -705,6 +668,6 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
assert theBits[0] == "The currently open file is saved in:"
|
assert theBits[0] == "The currently open file is saved in:"
|
||||||
assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd")
|
assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd")
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiMenu_Insert
|
# END Test testGuiMenu_Insert
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from tools import C, buildTestProject, writeFile
|
|||||||
|
|
||||||
from PyQt5.QtGui import QFocusEvent
|
from PyQt5.QtGui import QFocusEvent
|
||||||
from PyQt5.QtCore import Qt, QEvent
|
from PyQt5.QtCore import Qt, QEvent
|
||||||
from PyQt5.QtWidgets import QMessageBox, QToolTip
|
from PyQt5.QtWidgets import QToolTip
|
||||||
|
|
||||||
from novelwriter.enum import nwWidget, nwItemType
|
from novelwriter.enum import nwWidget, nwItemType
|
||||||
from novelwriter.gui.noveltree import NovelTreeColumn
|
from novelwriter.gui.noveltree import NovelTreeColumn
|
||||||
@@ -37,9 +37,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
|
|||||||
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||||
"""Test navigating the novel tree.
|
"""Test navigating the novel tree.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import pytest
|
|||||||
from tools import buildTestProject, writeFile
|
from tools import buildTestProject, writeFile
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QWidget, QMessageBox, QAction
|
from PyQt5.QtWidgets import QWidget, QAction
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwOutline, nwView
|
from novelwriter.enum import nwItemClass, nwOutline, nwView
|
||||||
|
|
||||||
@@ -35,10 +35,6 @@ from novelwriter.enum import nwItemClass, nwOutline, nwView
|
|||||||
def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
|
def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
|
||||||
"""Test the outline view.
|
"""Test the outline view.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
buildTestProject(nwGUI, prjDir)
|
buildTestProject(nwGUI, prjDir)
|
||||||
@@ -156,13 +152,9 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
def testGuiOutline_Content(qtbot, nwGUI, nwLipsum):
|
||||||
"""Test the outline view.
|
"""Test the outline view.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
assert nwGUI.openProject(nwLipsum)
|
assert nwGUI.openProject(nwLipsum)
|
||||||
nwGUI.mainConf.lastPath = nwLipsum
|
nwGUI.mainConf.lastPath = nwLipsum
|
||||||
|
|
||||||
@@ -183,7 +175,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
|
|
||||||
# Add a second novel folder
|
# Add a second novel folder
|
||||||
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
|
newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
|
||||||
nwGUI.projView.revealNewTreeItem(newHandle)
|
nwGUI.projView.projTree.revealNewTreeItem(newHandle)
|
||||||
|
|
||||||
# Check new values in dropdown list
|
# Check new values in dropdown list
|
||||||
assert outlineBar.novelValue.itemData(0) == lipHandle
|
assert outlineBar.novelValue.itemData(0) == lipHandle
|
||||||
@@ -202,7 +194,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
|
|||||||
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
|
aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
|
||||||
hHash = "#"*hLevel
|
hHash = "#"*hLevel
|
||||||
writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
|
writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
|
||||||
nwGUI.projView.revealNewTreeItem(aHandle)
|
nwGUI.projView.projTree.revealNewTreeItem(aHandle)
|
||||||
|
|
||||||
nwGUI.rebuildIndex()
|
nwGUI.rebuildIndex()
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
|
|||||||
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
"""Test adding and removing items from the project tree.
|
"""Test adding and removing items from the project tree.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
projView = nwGUI.projView
|
projView = nwGUI.projView
|
||||||
@@ -160,11 +155,11 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
|
|||||||
# ============
|
# ============
|
||||||
|
|
||||||
# Also check error handling in reveal function
|
# Also check error handling in reveal function
|
||||||
assert projView.revealNewTreeItem("abc") is False
|
assert projView.projTree.revealNewTreeItem("abc") is False
|
||||||
|
|
||||||
# Add an item that cannot be displayed in the tree
|
# Add an item that cannot be displayed in the tree
|
||||||
nHandle = theProject.newFile("Test", None)
|
nHandle = theProject.newFile("Test", None)
|
||||||
assert projView.revealNewTreeItem(nHandle) is False
|
assert projView.projTree.revealNewTreeItem(nHandle) is False
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
@@ -177,17 +172,13 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
|
|||||||
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
"""Test adding and removing items from the project tree.
|
"""Test adding and removing items from the project tree.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwTree = nwGUI.projView
|
projView = nwGUI.projView
|
||||||
|
projTree = nwGUI.projView.projTree
|
||||||
|
|
||||||
# Try to move item with no project
|
# Try to move item with no project
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert projView.projTree.moveTreeItem(1) is False
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
@@ -197,68 +188,68 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
# Add some files
|
# Add some files
|
||||||
nwTree.setSelectedHandle(C.hChapterDir)
|
projView.setSelectedHandle(C.hChapterDir)
|
||||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move with no selections
|
# Move with no selections
|
||||||
nwTree.projTree.clearSelection()
|
projTree.clearSelection()
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert projTree.moveTreeItem(1) is False
|
||||||
|
|
||||||
# Move second item up twice (should give same result)
|
# Move second item up twice (should give same result)
|
||||||
nwTree.setSelectedHandle(C.hSceneDoc)
|
projView.setSelectedHandle(C.hSceneDoc)
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
assert projTree.moveTreeItem(-1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is False
|
assert projTree.moveTreeItem(-1) is False
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
C.hChapterDir, C.hSceneDoc, C.hChapterDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Restore
|
# Restore
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move fifth item down twice (should give same result)
|
# Move fifth item down twice (should give same result)
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
projView.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert projTree.moveTreeItem(1) is False
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Restore
|
# Restore
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
assert projTree.moveTreeItem(-1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move down again, and restore via undo
|
# Move down again, and restore via undo
|
||||||
nwTree.setSelectedHandle("0000000000011")
|
projView.setSelectedHandle("0000000000011")
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000012", "0000000000011",
|
"0000000000010", "0000000000012", "0000000000011",
|
||||||
]
|
]
|
||||||
assert nwTree.projTree.undoLastMove() is True
|
assert projTree.undoLastMove() is True
|
||||||
assert nwTree.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
@@ -266,23 +257,23 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
# Root Folder
|
# Root Folder
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
nwTree.setSelectedHandle(C.hNovelRoot)
|
projView.setSelectedHandle(C.hNovelRoot)
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||||
|
|
||||||
# Move novel folder up
|
# Move novel folder up
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is False
|
assert projTree.moveTreeItem(-1) is False
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||||
|
|
||||||
# Move novel folder down
|
# Move novel folder down
|
||||||
assert nwTree.projTree.moveTreeItem(1) is True
|
assert projTree.moveTreeItem(1) is True
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 1
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 1
|
||||||
|
|
||||||
# Move novel folder up again
|
# Move novel folder up again
|
||||||
assert nwTree.projTree.moveTreeItem(-1) is True
|
assert projTree.moveTreeItem(-1) is True
|
||||||
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
nwGUI.closeProject()
|
nwGUI.closeProject()
|
||||||
|
|
||||||
# END Test testGuiProjTree_MoveItems
|
# END Test testGuiProjTree_MoveItems
|
||||||
@@ -292,83 +283,79 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
"""Test external requests for removing items from project tree.
|
"""Test external requests for removing items from project tree.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
nwView = nwGUI.projView
|
projView = nwGUI.projView
|
||||||
|
projTree = nwGUI.projView.projTree
|
||||||
|
|
||||||
# Try to run with no project
|
# Try to run with no project
|
||||||
assert nwView.requestDeleteItem() is False
|
assert projView.requestDeleteItem() is False
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
buildTestProject(nwGUI, prjDir)
|
buildTestProject(nwGUI, prjDir)
|
||||||
|
|
||||||
# Try emptying the trash already now, when there is no trash folder
|
# Try emptying the trash already now, when there is no trash folder
|
||||||
assert nwView.emptyTrash() is False
|
assert projView.emptyTrash() is False
|
||||||
|
|
||||||
# Add some files
|
# Add some files
|
||||||
nwView.setSelectedHandle(C.hChapterDir)
|
projView.setSelectedHandle(C.hChapterDir)
|
||||||
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwView.projTree.newTreeItem(nwItemType.FILE) is True
|
assert projTree.newTreeItem(nwItemType.FILE) is True
|
||||||
assert nwView.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Delete item without focus -> blocked
|
# Delete item without focus -> blocked
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
|
||||||
nwView.setSelectedHandle("0000000000012")
|
projView.setSelectedHandle("0000000000012")
|
||||||
assert nwView.requestDeleteItem() is False
|
assert projView.requestDeleteItem() is False
|
||||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||||
|
|
||||||
# No selection made
|
# No selection made
|
||||||
nwView.projTree.clearSelection()
|
projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwView.requestDeleteItem() is False
|
assert projView.requestDeleteItem() is False
|
||||||
assert "no item to delete" in caplog.text
|
assert "no item to delete" in caplog.text
|
||||||
|
|
||||||
# Not a valid handle
|
# Not a valid handle
|
||||||
nwView.projTree.clearSelection()
|
projTree.clearSelection()
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwView.requestDeleteItem("0000000000000") is False
|
assert projView.requestDeleteItem("0000000000000") is False
|
||||||
assert "No tree item with handle '0000000000000'" in caplog.text
|
assert "No tree item with handle '0000000000000'" in caplog.text
|
||||||
|
|
||||||
# Delete Root Folders
|
# Delete Root Folders
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
assert nwView.requestDeleteItem(C.hNovelRoot) is False # Novel Root is blocked
|
assert projView.requestDeleteItem(C.hNovelRoot) is False # Novel Root is blocked
|
||||||
assert nwView.requestDeleteItem(C.hCharRoot) is True # Character Root
|
assert projView.requestDeleteItem(C.hCharRoot) is True # Character Root
|
||||||
|
|
||||||
# Delete File
|
# Delete File
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
# Block adding trash folder
|
# Block adding trash folder
|
||||||
funcPointer = nwView.projTree._addTrashRoot
|
funcPointer = projTree._addTrashRoot
|
||||||
nwView.projTree._addTrashRoot = lambda *a: None
|
projTree._addTrashRoot = lambda *a: None
|
||||||
assert nwView.requestDeleteItem("0000000000012") is False
|
assert projView.requestDeleteItem("0000000000012") is False
|
||||||
nwView.projTree._addTrashRoot = funcPointer
|
projTree._addTrashRoot = funcPointer
|
||||||
|
|
||||||
# Delete last two documents, which also adds the trash folder
|
# Delete last two documents, which also adds the trash folder
|
||||||
assert nwView.requestDeleteItem("0000000000012") is True
|
assert projView.requestDeleteItem("0000000000012") is True
|
||||||
assert nwView.requestDeleteItem("0000000000011") is True
|
assert projView.requestDeleteItem("0000000000011") is True
|
||||||
assert nwView.getTreeFromHandle(C.hChapterDir) == [
|
assert projTree.getTreeFromHandle(C.hChapterDir) == [
|
||||||
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||||
"0000000000010"
|
"0000000000010"
|
||||||
]
|
]
|
||||||
trashHandle = nwGUI.theProject.tree.trashRoot()
|
trashHandle = nwGUI.theProject.tree.trashRoot()
|
||||||
assert nwView.getTreeFromHandle(trashHandle) == [
|
assert projTree.getTreeFromHandle(trashHandle) == [
|
||||||
trashHandle, "0000000000012", "0000000000011"
|
trashHandle, "0000000000012", "0000000000011"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Try to delete the trash folder
|
# Try to delete the trash folder
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert nwView.requestDeleteItem("0000000000013") is False
|
assert projView.requestDeleteItem("0000000000013") is False
|
||||||
assert "Cannot delete the Trash folder" in caplog.text
|
assert "Cannot delete the Trash folder" in caplog.text
|
||||||
|
|
||||||
nwGUI.closeProject()
|
nwGUI.closeProject()
|
||||||
@@ -380,11 +367,6 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir,
|
|||||||
def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
"""Test moving items to Trash.
|
"""Test moving items to Trash.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
theProject = nwGUI.theProject
|
theProject = nwGUI.theProject
|
||||||
@@ -439,11 +421,6 @@ def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, m
|
|||||||
def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
"""Test permanently deleting items.
|
"""Test permanently deleting items.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
theProject = nwGUI.theProject
|
theProject = nwGUI.theProject
|
||||||
@@ -497,11 +474,6 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fnc
|
|||||||
def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
|
||||||
"""Test emptying Trash.
|
"""Test emptying Trash.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
|
|
||||||
theProject = nwGUI.theProject
|
theProject = nwGUI.theProject
|
||||||
@@ -557,11 +529,6 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
"""Test the building of the project tree context menu. All this does
|
"""Test the building of the project tree context menu. All this does
|
||||||
is test that the menu builds. It doesn't open the actual menu,
|
is test that the menu builds. It doesn't open the actual menu,
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||||
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
|
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
|
||||||
|
|
||||||
@@ -682,12 +649,6 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, i
|
|||||||
"""
|
"""
|
||||||
mergeData = {}
|
mergeData = {}
|
||||||
|
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None)
|
monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.Accepted)
|
||||||
@@ -792,12 +753,6 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, ip
|
|||||||
splitData = {}
|
splitData = {}
|
||||||
splitText = []
|
splitText = []
|
||||||
|
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None)
|
monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QDialog.Accepted)
|
||||||
@@ -882,14 +837,17 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, ip
|
|||||||
assert tHandle in theProject.tree
|
assert tHandle in theProject.tree
|
||||||
assert os.path.isfile(os.path.join(prjDir, "content", f"{tHandle}.nwd"))
|
assert os.path.isfile(os.path.join(prjDir, "content", f"{tHandle}.nwd"))
|
||||||
|
|
||||||
# Add to a folder
|
# Add to a folder and move source to trash
|
||||||
splitData["intoFolder"] = True
|
splitData["intoFolder"] = True
|
||||||
|
splitData["moveToTrash"] = True
|
||||||
assert projTree._splitDocument(hSplitDoc) is True
|
assert projTree._splitDocument(hSplitDoc) is True
|
||||||
assert "0000000000029" in theProject.tree # The folder
|
assert "0000000000029" in theProject.tree # The folder
|
||||||
for tHandle in trdSet:
|
for tHandle in trdSet:
|
||||||
assert tHandle in theProject.tree
|
assert tHandle in theProject.tree
|
||||||
assert os.path.isfile(os.path.join(prjDir, "content", f"{tHandle}.nwd"))
|
assert os.path.isfile(os.path.join(prjDir, "content", f"{tHandle}.nwd"))
|
||||||
|
|
||||||
|
assert theProject.tree.isTrash(hSplitDoc) is True
|
||||||
|
|
||||||
# Cancelled by user
|
# Cancelled by user
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(GuiDocSplit, "result", lambda *a: QDialog.Rejected)
|
mp.setattr(GuiDocSplit, "result", lambda *a: QDialog.Rejected)
|
||||||
@@ -905,12 +863,6 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
"""Test various parts of the project tree class not covered by
|
"""Test various parts of the project tree class not covered by
|
||||||
other tests.
|
other tests.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Create a project
|
# Create a project
|
||||||
prjDir = os.path.join(fncDir, "project")
|
prjDir = os.path.join(fncDir, "project")
|
||||||
buildTestProject(nwGUI, prjDir)
|
buildTestProject(nwGUI, prjDir)
|
||||||
|
|||||||
@@ -24,23 +24,19 @@ import pytest
|
|||||||
|
|
||||||
from tools import C, buildTestProject
|
from tools import C, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
from novelwriter.enum import nwState
|
from novelwriter.enum import nwState
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testGuiStatusBar_Main(qtbot, nwGUI, fncProj, mockRnd):
|
||||||
"""Test the the various features of the status bar.
|
"""Test the the various features of the status bar.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
|
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
|
||||||
newDoc = NWDoc(nwGUI.theProject, cHandle)
|
newDoc = NWDoc(nwGUI.theProject, cHandle)
|
||||||
newDoc.writeDocument("# A Note\n\n")
|
newDoc.writeDocument("# A Note\n\n")
|
||||||
nwGUI.projView.revealNewTreeItem(cHandle)
|
nwGUI.projView.projTree.revealNewTreeItem(cHandle)
|
||||||
nwGUI.rebuildIndex(beQuiet=True)
|
nwGUI.rebuildIndex(beQuiet=True)
|
||||||
|
|
||||||
# Reference Time
|
# Reference Time
|
||||||
|
|||||||
@@ -19,161 +19,428 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
import pytest
|
import pytest
|
||||||
import novelwriter
|
|
||||||
|
|
||||||
from PyQt5.QtGui import QColor, QPixmap, QIcon
|
from configparser import ConfigParser
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
keyDelay = 2
|
from mock import causeOSError
|
||||||
typeDelay = 1
|
from novelwriter.constants import nwLabels
|
||||||
stepDelay = 20
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||||
|
from tools import writeFile
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QIcon, QPalette, QPixmap
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
|
from novelwriter.config import Config
|
||||||
|
from novelwriter.gui.theme import GuiIcons, GuiTheme
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
|
def testGuiTheme_Main(qtbot, nwGUI, fncDir):
|
||||||
"""Test the theme and icon classes.
|
"""Test the theme class init.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
mainTheme: GuiTheme = nwGUI.mainTheme
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
mainConf: Config = nwGUI.mainConf
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
nwGUI = novelwriter.main(
|
# Methods
|
||||||
["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]
|
# =======
|
||||||
)
|
|
||||||
qtbot.addWidget(nwGUI)
|
|
||||||
nwGUI.show()
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Change Settings
|
mSize = mainTheme.getTextWidth("m")
|
||||||
assert novelwriter.CONFIG.confPath == nwMinimal
|
assert mSize > 0
|
||||||
novelwriter.CONFIG.guiTheme = "default_dark"
|
assert mainTheme.getTextWidth("m", mainTheme.guiFont) == mSize
|
||||||
novelwriter.CONFIG.guiSyntax = "tomorrow_night_eighties"
|
|
||||||
novelwriter.CONFIG.guiIcons = "typicons_colour_dark"
|
|
||||||
novelwriter.CONFIG.guiFont = "Cantarell"
|
|
||||||
novelwriter.CONFIG.guiFontSize = 11
|
|
||||||
novelwriter.CONFIG.confChanged = True
|
|
||||||
assert novelwriter.CONFIG.saveConfig()
|
|
||||||
|
|
||||||
nwGUI.closeMain()
|
# Init Fonts
|
||||||
nwGUI.close()
|
# ==========
|
||||||
del nwGUI
|
|
||||||
|
|
||||||
# Re-open
|
# The defaults should be set
|
||||||
assert novelwriter.CONFIG.confPath == nwMinimal
|
defaultFont = mainConf.guiFont
|
||||||
nwGUI = novelwriter.main(
|
defaultSize = mainConf.guiFontSize
|
||||||
["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]
|
|
||||||
)
|
|
||||||
assert nwGUI.mainConf.confPath == nwMinimal
|
|
||||||
qtbot.addWidget(nwGUI)
|
|
||||||
nwGUI.show()
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
assert novelwriter.CONFIG.guiTheme == "default_dark"
|
# CHange them to nonsense values
|
||||||
assert novelwriter.CONFIG.guiSyntax == "tomorrow_night_eighties"
|
mainConf.guiFont = "notafont"
|
||||||
assert novelwriter.CONFIG.guiIcons == "typicons_dark"
|
mainConf.guiFontSize = 99
|
||||||
assert novelwriter.CONFIG.guiFont != ""
|
|
||||||
assert novelwriter.CONFIG.guiFontSize > 0
|
|
||||||
|
|
||||||
# Check GUI Colours
|
# Let the theme class set them back to default
|
||||||
thePalette = nwGUI.palette()
|
mainTheme._setGuiFont()
|
||||||
assert thePalette.window().color() == QColor(54, 54, 54)
|
assert mainConf.guiFont == defaultFont
|
||||||
assert thePalette.windowText().color() == QColor(174, 174, 174)
|
assert mainConf.guiFontSize == defaultSize
|
||||||
assert thePalette.base().color() == QColor(62, 62, 62)
|
|
||||||
assert thePalette.alternateBase().color() == QColor(78, 78, 78)
|
|
||||||
assert thePalette.text().color() == QColor(174, 174, 174)
|
|
||||||
assert thePalette.toolTipBase().color() == QColor(255, 255, 192)
|
|
||||||
assert thePalette.toolTipText().color() == QColor(21, 21, 13)
|
|
||||||
assert thePalette.button().color() == QColor(62, 62, 62)
|
|
||||||
assert thePalette.buttonText().color() == QColor(174, 174, 174)
|
|
||||||
assert thePalette.brightText().color() == QColor(174, 174, 174)
|
|
||||||
assert thePalette.highlight().color() == QColor(44, 152, 247)
|
|
||||||
assert thePalette.highlightedText().color() == QColor(255, 255, 255)
|
|
||||||
assert thePalette.link().color() == QColor(44, 152, 247)
|
|
||||||
assert thePalette.linkVisited().color() == QColor(44, 152, 247)
|
|
||||||
|
|
||||||
assert nwGUI.mainTheme.statNone == [150, 152, 150]
|
# A second call should just restore the defaults again
|
||||||
assert nwGUI.mainTheme.statSaved == [39, 135, 78]
|
mainTheme._setGuiFont()
|
||||||
assert nwGUI.mainTheme.statUnsaved == [138, 32, 32]
|
assert mainConf.guiFont == defaultFont
|
||||||
|
assert mainConf.guiFontSize == defaultSize
|
||||||
|
|
||||||
# Check Syntax Colours
|
# Scan for Themes
|
||||||
assert nwGUI.mainTheme.colBack == [45, 45, 45]
|
# ===============
|
||||||
assert nwGUI.mainTheme.colText == [204, 204, 204]
|
|
||||||
assert nwGUI.mainTheme.colLink == [102, 153, 204]
|
|
||||||
assert nwGUI.mainTheme.colHead == [102, 153, 204]
|
|
||||||
assert nwGUI.mainTheme.colHeadH == [102, 153, 204]
|
|
||||||
assert nwGUI.mainTheme.colEmph == [249, 145, 57]
|
|
||||||
assert nwGUI.mainTheme.colDialN == [242, 119, 122]
|
|
||||||
assert nwGUI.mainTheme.colDialD == [153, 204, 153]
|
|
||||||
assert nwGUI.mainTheme.colDialS == [255, 204, 102]
|
|
||||||
assert nwGUI.mainTheme.colHidden == [153, 153, 153]
|
|
||||||
assert nwGUI.mainTheme.colKey == [242, 119, 122]
|
|
||||||
assert nwGUI.mainTheme.colVal == [204, 153, 204]
|
|
||||||
assert nwGUI.mainTheme.colSpell == [242, 119, 122]
|
|
||||||
assert nwGUI.mainTheme.colError == [153, 204, 153]
|
|
||||||
assert nwGUI.mainTheme.colRepTag == [102, 204, 204]
|
|
||||||
assert nwGUI.mainTheme.colMod == [249, 145, 57]
|
|
||||||
|
|
||||||
# Test Icon class
|
assert mainTheme._listConf({}, "not_a_path") is False
|
||||||
iconCache = nwGUI.mainTheme.iconCache
|
|
||||||
novelwriter.CONFIG.guiIcons = "invalid"
|
|
||||||
assert iconCache.updateTheme() is True
|
|
||||||
assert novelwriter.CONFIG.guiIcons == "typicons_light"
|
|
||||||
|
|
||||||
# Ask for a non-existent key
|
themeOne = os.path.join(fncDir, "themes", "themeone.conf")
|
||||||
anImg = iconCache.loadDecoration("nonsense", 20, 20)
|
themeTwo = os.path.join(fncDir, "themes", "themetwo.conf")
|
||||||
assert isinstance(anImg, QPixmap)
|
writeFile(themeOne, "# Stuff")
|
||||||
assert anImg.isNull()
|
writeFile(themeTwo, "# Stuff")
|
||||||
|
|
||||||
# Add a non-existent file and request it
|
result = {}
|
||||||
iconCache.IMAGE_MAP["nonsense"] = "nofile.jpg"
|
assert mainTheme._listConf(result, os.path.join(fncDir, "themes")) is True
|
||||||
anImg = iconCache.loadDecoration("nonsense", 20, 20)
|
assert result["themeone"] == themeOne
|
||||||
assert isinstance(anImg, QPixmap)
|
assert result["themetwo"] == themeTwo
|
||||||
assert anImg.isNull()
|
|
||||||
|
|
||||||
# Get a real image, with different size parameters
|
# Parse Colours
|
||||||
anImg = iconCache.loadDecoration("wiz-back", 20, None)
|
# =============
|
||||||
assert isinstance(anImg, QPixmap)
|
|
||||||
assert not anImg.isNull()
|
|
||||||
assert anImg.width() == 20
|
|
||||||
assert anImg.height() >= 56
|
|
||||||
|
|
||||||
anImg = iconCache.loadDecoration("wiz-back", None, 70)
|
parser = ConfigParser()
|
||||||
assert isinstance(anImg, QPixmap)
|
parser["Palette"] = {
|
||||||
assert not anImg.isNull()
|
"colour1": "100, 150, 200",
|
||||||
assert anImg.height() == 70
|
"colour2": "100, 150, 200, 250",
|
||||||
assert anImg.width() >= 24
|
"colour3": "250, 250",
|
||||||
|
"colour4": "-10, 127, 300",
|
||||||
|
}
|
||||||
|
|
||||||
anImg = iconCache.loadDecoration("wiz-back", 30, 70)
|
# Test the parser for several valid and invalid values
|
||||||
assert isinstance(anImg, QPixmap)
|
assert mainTheme._parseColour(parser, "Palette", "colour1") == [100, 150, 200]
|
||||||
assert not anImg.isNull()
|
assert mainTheme._parseColour(parser, "Palette", "colour2") == [100, 150, 200]
|
||||||
assert anImg.height() == 70
|
assert mainTheme._parseColour(parser, "Palette", "colour3") == [0, 0, 0]
|
||||||
assert anImg.width() == 30
|
assert mainTheme._parseColour(parser, "Palette", "colour4") == [0, 127, 255]
|
||||||
|
assert mainTheme._parseColour(parser, "Palette", "colour5") == [0, 0, 0]
|
||||||
|
|
||||||
anImg = iconCache.loadDecoration("wiz-back", None, None)
|
# The palette should load with the parsed values
|
||||||
assert isinstance(anImg, QPixmap)
|
mainTheme._setPalette(parser, "Palette", "colour1", QPalette.Window)
|
||||||
assert not anImg.isNull()
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (100, 150, 200, 255)
|
||||||
assert anImg.height() >= 1500
|
mainTheme._setPalette(parser, "Palette", "colour2", QPalette.Window)
|
||||||
assert anImg.width() >= 500
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (100, 150, 200, 255)
|
||||||
|
mainTheme._setPalette(parser, "Palette", "colour3", QPalette.Window)
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 0, 0, 255)
|
||||||
|
mainTheme._setPalette(parser, "Palette", "colour4", QPalette.Window)
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 127, 255, 255)
|
||||||
|
mainTheme._setPalette(parser, "Palette", "colour5", QPalette.Window)
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 0, 0, 255)
|
||||||
|
|
||||||
# Load icons
|
# qtbot.stop()
|
||||||
anIcon = iconCache.getIcon("nonsense")
|
|
||||||
assert isinstance(anIcon, QIcon)
|
|
||||||
assert anIcon.isNull()
|
|
||||||
|
|
||||||
anIcon = iconCache.getIcon("novelwriter")
|
|
||||||
assert isinstance(anIcon, QIcon)
|
|
||||||
assert not anIcon.isNull()
|
|
||||||
|
|
||||||
# Check return empty icon if file not found
|
|
||||||
iconCache.ICON_KEYS.add("testicon3")
|
|
||||||
anIcon = iconCache.getIcon("testicon3")
|
|
||||||
assert isinstance(anIcon, QIcon)
|
|
||||||
assert anIcon.isNull()
|
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
|
|
||||||
# END Test testGuiTheme_Main
|
# END Test testGuiTheme_Main
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncDir):
|
||||||
|
"""Test the theme part of the class.
|
||||||
|
"""
|
||||||
|
mainTheme: GuiTheme = nwGUI.mainTheme
|
||||||
|
mainConf: Config = nwGUI.mainConf
|
||||||
|
|
||||||
|
# List Themes
|
||||||
|
# ===========
|
||||||
|
|
||||||
|
shutil.copy(
|
||||||
|
os.path.join(mainConf.assetPath, "themes", "default_dark.conf"),
|
||||||
|
os.path.join(fncDir, "themes")
|
||||||
|
)
|
||||||
|
shutil.copy(
|
||||||
|
os.path.join(mainConf.assetPath, "themes", "default.conf"),
|
||||||
|
os.path.join(fncDir, "themes")
|
||||||
|
)
|
||||||
|
writeFile(os.path.join(fncDir, "themes", "default.qss"), "/* Stuff */")
|
||||||
|
|
||||||
|
# Block the reading of the files
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
assert mainTheme.listThemes() == []
|
||||||
|
|
||||||
|
# Load the theme info
|
||||||
|
themesList = mainTheme.listThemes()
|
||||||
|
assert themesList[0] == ("default_dark", "Default Dark Theme")
|
||||||
|
assert themesList[1] == ("default", "Default Theme")
|
||||||
|
|
||||||
|
# A second call should returned the cached list
|
||||||
|
assert mainTheme.listThemes() == mainTheme._themeList
|
||||||
|
|
||||||
|
# Check handling of broken theme settings
|
||||||
|
mainConf.guiTheme = "not_a_theme"
|
||||||
|
assert mainTheme.loadTheme() is False
|
||||||
|
|
||||||
|
# Check handling of unreadable file
|
||||||
|
mainConf.guiTheme = "default"
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
assert mainTheme.loadTheme() is False
|
||||||
|
|
||||||
|
# Load Default Theme
|
||||||
|
# ==================
|
||||||
|
|
||||||
|
# Set a mock colour for the window background
|
||||||
|
mainTheme._guiPalette.color(QPalette.Window).setRgb(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Load the default theme
|
||||||
|
mainConf.guiTheme = "default"
|
||||||
|
assert mainTheme.loadTheme() is True
|
||||||
|
|
||||||
|
# This should load a standard palette
|
||||||
|
wCol = QApplication.style().standardPalette().color(QPalette.Window).getRgb()
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == wCol
|
||||||
|
|
||||||
|
# Load Default Dark Theme
|
||||||
|
# =======================
|
||||||
|
|
||||||
|
mainConf.guiTheme = "default_dark"
|
||||||
|
assert mainTheme.loadTheme() is True
|
||||||
|
|
||||||
|
# Check a few values
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (54, 54, 54, 255)
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.WindowText).getRgb() == (174, 174, 174, 255)
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.Base).getRgb() == (62, 62, 62, 255)
|
||||||
|
assert mainTheme._guiPalette.color(QPalette.AlternateBase).getRgb() == (78, 78, 78, 255)
|
||||||
|
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testGuiTheme_Theme
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncDir):
|
||||||
|
"""Test the syntax part of the class.
|
||||||
|
"""
|
||||||
|
mainTheme: GuiTheme = nwGUI.mainTheme
|
||||||
|
mainConf: Config = nwGUI.mainConf
|
||||||
|
|
||||||
|
# List Themes
|
||||||
|
# ===========
|
||||||
|
|
||||||
|
shutil.copy(
|
||||||
|
os.path.join(mainConf.assetPath, "syntax", "default_dark.conf"),
|
||||||
|
os.path.join(fncDir, "syntax")
|
||||||
|
)
|
||||||
|
shutil.copy(
|
||||||
|
os.path.join(mainConf.assetPath, "syntax", "default_light.conf"),
|
||||||
|
os.path.join(fncDir, "syntax")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Block the reading of the files
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
assert mainTheme.listThemes() == []
|
||||||
|
|
||||||
|
# Load the syntax info
|
||||||
|
syntaxList = mainTheme.listSyntax()
|
||||||
|
assert syntaxList[0] == ("default_dark", "Default Dark")
|
||||||
|
assert syntaxList[1] == ("default_light", "Default Light")
|
||||||
|
|
||||||
|
# A second call should returned the cached list
|
||||||
|
assert mainTheme.listSyntax() == mainTheme._syntaxList
|
||||||
|
|
||||||
|
# Check handling of broken theme settings
|
||||||
|
mainConf.guiSyntax = "not_a_syntax"
|
||||||
|
assert mainTheme.loadSyntax() is False
|
||||||
|
|
||||||
|
# Check handling of unreadable file
|
||||||
|
mainConf.guiSyntax = "default_light"
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
assert mainTheme.loadSyntax() is False
|
||||||
|
|
||||||
|
# Load Default Light Syntax
|
||||||
|
# =========================
|
||||||
|
|
||||||
|
# Load the default syntax
|
||||||
|
mainConf.guiSyntax = "default_light"
|
||||||
|
assert mainTheme.loadSyntax() is True
|
||||||
|
|
||||||
|
# Check some values
|
||||||
|
assert mainTheme.syntaxName == "Default Light"
|
||||||
|
assert mainTheme.colBack == [255, 255, 255]
|
||||||
|
assert mainTheme.colText == [0, 0, 0]
|
||||||
|
assert mainTheme.colLink == [0, 0, 200]
|
||||||
|
|
||||||
|
# Load Default Dark Theme
|
||||||
|
# =======================
|
||||||
|
|
||||||
|
# Load the default syntax
|
||||||
|
mainConf.guiSyntax = "default_dark"
|
||||||
|
assert mainTheme.loadSyntax() is True
|
||||||
|
|
||||||
|
# Check some values
|
||||||
|
assert mainTheme.syntaxName == "Default Dark"
|
||||||
|
assert mainTheme.colBack == [54, 54, 54]
|
||||||
|
assert mainTheme.colText == [199, 207, 208]
|
||||||
|
assert mainTheme.colLink == [184, 200, 0]
|
||||||
|
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testGuiTheme_Syntax
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
|
||||||
|
"""Test the icon cache class.
|
||||||
|
"""
|
||||||
|
iconCache: GuiIcons = nwGUI.mainTheme.iconCache
|
||||||
|
mainConf: Config = nwGUI.mainConf
|
||||||
|
|
||||||
|
# Load Theme
|
||||||
|
# ==========
|
||||||
|
|
||||||
|
# Invalid theme name
|
||||||
|
assert iconCache.loadTheme("not_a_theme") is False
|
||||||
|
|
||||||
|
# Check handling of unreadable file
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
assert iconCache.loadTheme("typicons_dark") is False
|
||||||
|
|
||||||
|
# Load a broken theme file
|
||||||
|
iconsDir = os.path.join(fncDir, "icons")
|
||||||
|
os.mkdir(iconsDir)
|
||||||
|
os.mkdir(os.path.join(iconsDir, "testicons"))
|
||||||
|
writeFile(os.path.join(iconsDir, "testicons", "icons.conf"), (
|
||||||
|
"[Main]\n"
|
||||||
|
"name = Test Icons\n"
|
||||||
|
"\n"
|
||||||
|
"[Map]\n"
|
||||||
|
"add = add.svg\n"
|
||||||
|
"stuff = stuff.svg\n"
|
||||||
|
))
|
||||||
|
|
||||||
|
assetPath = mainConf.assetPath
|
||||||
|
mainConf.assetPath = fncDir
|
||||||
|
|
||||||
|
caplog.clear()
|
||||||
|
assert iconCache.loadTheme("testicons") is True
|
||||||
|
assert "Unknown icon name 'stuff' in config file" in caplog.text
|
||||||
|
assert "Icon file 'add.svg' not in theme folder" in caplog.text
|
||||||
|
|
||||||
|
mainConf.assetPath = assetPath
|
||||||
|
|
||||||
|
# Load working theme file
|
||||||
|
assert iconCache.loadTheme("typicons_dark") is True
|
||||||
|
assert "add" in iconCache._themeMap
|
||||||
|
|
||||||
|
# Load Decorations
|
||||||
|
# ================
|
||||||
|
|
||||||
|
# Invalid name should return empty pixmap
|
||||||
|
qPix = iconCache.loadDecoration("stuff")
|
||||||
|
assert qPix.isNull() is True
|
||||||
|
|
||||||
|
# Load an image
|
||||||
|
qPix = iconCache.loadDecoration("wiz-back")
|
||||||
|
assert qPix.isNull() is False
|
||||||
|
|
||||||
|
# Fail finding the file
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("os.path.isfile", lambda *a: False)
|
||||||
|
qPix = iconCache.loadDecoration("wiz-back")
|
||||||
|
assert qPix.isNull() is True
|
||||||
|
|
||||||
|
# Test image sizes
|
||||||
|
qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=None)
|
||||||
|
assert qPix.isNull() is False
|
||||||
|
assert qPix.width() == 100
|
||||||
|
assert qPix.height() > 100
|
||||||
|
|
||||||
|
qPix = iconCache.loadDecoration("wiz-back", pxW=None, pxH=100)
|
||||||
|
assert qPix.isNull() is False
|
||||||
|
assert qPix.width() < 100
|
||||||
|
assert qPix.height() == 100
|
||||||
|
|
||||||
|
qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=100)
|
||||||
|
assert qPix.isNull() is False
|
||||||
|
assert qPix.width() == 100
|
||||||
|
assert qPix.height() == 100
|
||||||
|
|
||||||
|
# Load Icons
|
||||||
|
# ==========
|
||||||
|
|
||||||
|
# Load an unknown icon
|
||||||
|
qIcon = iconCache.getIcon("stuff")
|
||||||
|
assert isinstance(qIcon, QIcon)
|
||||||
|
assert qIcon.isNull() is True
|
||||||
|
|
||||||
|
# Load an icon, it is likelyu already cached
|
||||||
|
qIcon = iconCache.getIcon("add")
|
||||||
|
assert isinstance(qIcon, QIcon)
|
||||||
|
assert qIcon.isNull() is False
|
||||||
|
|
||||||
|
# Load it as a pixmap with a size
|
||||||
|
qPix = iconCache.getPixmap("add", (50, 50))
|
||||||
|
assert isinstance(qPix, QPixmap)
|
||||||
|
assert qPix.isNull() is False
|
||||||
|
assert qPix.width() == 50
|
||||||
|
assert qPix.height() == 50
|
||||||
|
|
||||||
|
# Load app icon
|
||||||
|
qIcon = iconCache.getIcon("novelwriter")
|
||||||
|
assert isinstance(qIcon, QIcon)
|
||||||
|
assert qIcon.isNull() is False
|
||||||
|
|
||||||
|
# Load mime icon
|
||||||
|
qIcon = iconCache.getIcon("proj_nwx")
|
||||||
|
assert isinstance(qIcon, QIcon)
|
||||||
|
assert qIcon.isNull() is False
|
||||||
|
|
||||||
|
# Load Item Icons
|
||||||
|
# ===============
|
||||||
|
|
||||||
|
# Root -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.ROOT, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
|
||||||
|
) == iconCache.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
|
||||||
|
|
||||||
|
# Folder -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FOLDER, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
|
||||||
|
) == iconCache.getIcon("proj_folder")
|
||||||
|
|
||||||
|
# Document H0 -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
|
||||||
|
) == iconCache.getIcon("proj_document")
|
||||||
|
|
||||||
|
# Document H1 -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H1"
|
||||||
|
) == iconCache.getIcon("proj_title")
|
||||||
|
|
||||||
|
# Document H2 -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H2"
|
||||||
|
) == iconCache.getIcon("proj_chapter")
|
||||||
|
|
||||||
|
# Document H3 -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H3"
|
||||||
|
) == iconCache.getIcon("proj_scene")
|
||||||
|
|
||||||
|
# Document H4 -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H4"
|
||||||
|
) == iconCache.getIcon("proj_section")
|
||||||
|
|
||||||
|
# Document H5 -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H4"
|
||||||
|
) == iconCache.getIcon("proj_document")
|
||||||
|
|
||||||
|
# Note -> Not Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NOTE, hLevel="H5"
|
||||||
|
) == iconCache.getIcon("proj_note")
|
||||||
|
|
||||||
|
# No Type -> Null
|
||||||
|
assert iconCache.getItemIcon(
|
||||||
|
nwItemType.NO_TYPE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H0"
|
||||||
|
).isNull() is True
|
||||||
|
|
||||||
|
# Header Decorations
|
||||||
|
# ==================
|
||||||
|
|
||||||
|
assert iconCache.getHeaderDecoration(-1) == iconCache._headerDec[0]
|
||||||
|
assert iconCache.getHeaderDecoration(0) == iconCache._headerDec[0]
|
||||||
|
assert iconCache.getHeaderDecoration(1) == iconCache._headerDec[1]
|
||||||
|
assert iconCache.getHeaderDecoration(2) == iconCache._headerDec[2]
|
||||||
|
assert iconCache.getHeaderDecoration(3) == iconCache._headerDec[3]
|
||||||
|
assert iconCache.getHeaderDecoration(4) == iconCache._headerDec[4]
|
||||||
|
assert iconCache.getHeaderDecoration(5) == iconCache._headerDec[4]
|
||||||
|
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testGuiTheme_Icons
|
||||||
|
|||||||
@@ -26,22 +26,16 @@ from shutil import copyfile
|
|||||||
from tools import cmpFiles, getGuiItem
|
from tools import cmpFiles, getGuiItem
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox, QFileDialog
|
from PyQt5.QtWidgets import QAction, QFileDialog
|
||||||
|
|
||||||
from novelwriter.tools import GuiBuildNovel
|
from novelwriter.tools import GuiBuildNovel
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
|
def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
|
||||||
"""Test the build tool.
|
"""Test the build tool.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
# Block message box
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **k: (c, None))
|
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **k: (c, None))
|
||||||
|
|
||||||
# Check that we cannot open when there is no project
|
# Check that we cannot open when there is no project
|
||||||
@@ -121,27 +115,17 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
|
|||||||
|
|
||||||
# Change Title Formats and Flip Switches
|
# Change Title Formats and Flip Switches
|
||||||
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
|
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwBuild.fmtScene.setText(r"Scene %ch%.%sc%: %title%")
|
nwBuild.fmtScene.setText(r"Scene %ch%.%sc%: %title%")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwBuild.fmtSection.setText(r"%ch%.%sc%.1: %title%")
|
nwBuild.fmtSection.setText(r"%ch%.%sc%.1: %title%")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
qtbot.mouseClick(nwBuild.justifyText, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.justifyText, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.includeSynopsis, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.includeSynopsis, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.includeKeywords, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.includeKeywords, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.replaceUCode, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.replaceUCode, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
|
||||||
|
|
||||||
@@ -175,7 +159,6 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
|
|||||||
|
|
||||||
# Replace Tabs with Spaces
|
# Replace Tabs with Spaces
|
||||||
qtbot.mouseClick(nwBuild.replaceTabs, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.replaceTabs, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
|
||||||
|
|
||||||
@@ -210,20 +193,13 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
|
|||||||
|
|
||||||
# Putline Mode
|
# Putline Mode
|
||||||
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
|
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwBuild.fmtScene.setText(r"Scene %sca%: %title%")
|
nwBuild.fmtScene.setText(r"Scene %sca%: %title%")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
nwBuild.fmtSection.setText(r"Section: %title%")
|
nwBuild.fmtSection.setText(r"Section: %title%")
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwBuild.includeBody, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.includeBody, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
|
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
|
||||||
|
|
||||||
@@ -287,6 +263,6 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
|
|||||||
|
|
||||||
nwBuild._doClose()
|
nwBuild._doClose()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testToolBuild_Main
|
# END Test testToolBuild_Main
|
||||||
|
|||||||
@@ -23,18 +23,15 @@ import pytest
|
|||||||
|
|
||||||
from tools import C, getGuiItem, buildTestProject
|
from tools import C, getGuiItem, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QAction, QMessageBox
|
from PyQt5.QtWidgets import QAction
|
||||||
|
|
||||||
from novelwriter.tools import GuiLipsum
|
from novelwriter.tools import GuiLipsum
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
def testToolLipsum_Main(qtbot, nwGUI, fncProj, mockRnd):
|
||||||
"""Test the Lorem Ipsum tool.
|
"""Test the Lorem Ipsum tool.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Check that we cannot open when there is no project
|
# Check that we cannot open when there is no project
|
||||||
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
|
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
|
||||||
assert getGuiItem("GuiLipsum") is None
|
assert getGuiItem("GuiLipsum") is None
|
||||||
@@ -70,6 +67,6 @@ def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
|||||||
# Close
|
# Close
|
||||||
nwLipsum._doClose()
|
nwLipsum._doClose()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testToolLipsum_Main
|
# END Test testToolLipsum_Main
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import pytest
|
|||||||
from tools import getGuiItem
|
from tools import getGuiItem
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QFileDialog, QWizard, QMessageBox, QDialog
|
from PyQt5.QtWidgets import QFileDialog, QWizard, QDialog
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass
|
from novelwriter.enum import nwItemClass
|
||||||
from novelwriter.tools.projwizard import (
|
from novelwriter.tools.projwizard import (
|
||||||
@@ -34,10 +34,6 @@ from novelwriter.tools.projwizard import (
|
|||||||
ProjWizardPopulatePage, ProjWizardCustomPage, ProjWizardFinalPage
|
ProjWizardPopulatePage, ProjWizardCustomPage, ProjWizardFinalPage
|
||||||
)
|
)
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin")
|
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin")
|
||||||
@@ -45,13 +41,8 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
"""Test the launch of the project wizard.
|
"""Test the launch of the project wizard.
|
||||||
Disabled for macOS because the test segfaults on QWizard.show()
|
Disabled for macOS because the test segfaults on QWizard.show()
|
||||||
"""
|
"""
|
||||||
# Block message box
|
# Test New Project Function
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
# ========================
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
##
|
|
||||||
# Test New Project Function
|
|
||||||
##
|
|
||||||
|
|
||||||
# New with a project open should cause an error
|
# New with a project open should cause an error
|
||||||
assert nwGUI.openProject(nwMinimal)
|
assert nwGUI.openProject(nwMinimal)
|
||||||
@@ -73,9 +64,8 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal})
|
mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal})
|
||||||
assert nwGUI.newProject() is False
|
assert nwGUI.newProject() is False
|
||||||
|
|
||||||
##
|
# Test the Wizard Launching
|
||||||
# Test the Wizard Launching
|
# =========================
|
||||||
##
|
|
||||||
|
|
||||||
nwGUI.mainConf.lastPath = " "
|
nwGUI.mainConf.lastPath = " "
|
||||||
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
|
||||||
@@ -87,7 +77,6 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
assert isinstance(nwWiz, GuiProjectWizard)
|
assert isinstance(nwWiz, GuiProjectWizard)
|
||||||
nwWiz.show()
|
nwWiz.show()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwWiz.button(QWizard.CancelButton), Qt.LeftButton)
|
qtbot.mouseClick(nwWiz.button(QWizard.CancelButton), Qt.LeftButton)
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
@@ -100,7 +89,7 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
nwWiz.reject()
|
nwWiz.reject()
|
||||||
nwWiz.close()
|
nwWiz.close()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testToolProjectWizard_Handling
|
# END Test testToolProjectWizard_Handling
|
||||||
|
|
||||||
@@ -111,14 +100,12 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
||||||
"""Test the new project wizard with a set of selection scenarios.
|
"""Test the new project wizard with a set of selection scenarios.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
|
||||||
|
|
||||||
nwGUI.mainConf.lastPath = " "
|
nwGUI.mainConf.lastPath = " "
|
||||||
nwWiz = GuiProjectWizard(nwGUI)
|
nwWiz = GuiProjectWizard(nwGUI)
|
||||||
nwWiz.show()
|
nwWiz.show()
|
||||||
qtbot.wait(stepDelay)
|
qtbot.addWidget(nwWiz)
|
||||||
|
|
||||||
# Intro Page
|
# Intro Page
|
||||||
# ==========
|
# ==========
|
||||||
@@ -134,7 +121,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
|||||||
# Setting projName should activate the button
|
# Setting projName should activate the button
|
||||||
assert nwWiz.button(QWizard.NextButton).isEnabled()
|
assert nwWiz.button(QWizard.NextButton).isEnabled()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
||||||
|
|
||||||
# Folder Page
|
# Folder Page
|
||||||
@@ -173,7 +159,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
|||||||
# Setting projPath should activate the button
|
# Setting projPath should activate the button
|
||||||
assert nwWiz.button(QWizard.NextButton).isEnabled()
|
assert nwWiz.button(QWizard.NextButton).isEnabled()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
||||||
|
|
||||||
# Populate Page
|
# Populate Page
|
||||||
@@ -183,7 +168,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
|||||||
assert isinstance(popPage, ProjWizardPopulatePage)
|
assert isinstance(popPage, ProjWizardPopulatePage)
|
||||||
assert nwWiz.button(QWizard.NextButton).isEnabled()
|
assert nwWiz.button(QWizard.NextButton).isEnabled()
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
if prjType.startswith("minimal"):
|
if prjType.startswith("minimal"):
|
||||||
popPage.popMinimal.setChecked(True)
|
popPage.popMinimal.setChecked(True)
|
||||||
elif prjType.startswith("custom"):
|
elif prjType.startswith("custom"):
|
||||||
@@ -191,7 +175,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
|||||||
elif prjType.startswith("sample"):
|
elif prjType.startswith("sample"):
|
||||||
popPage.popSample.setChecked(True)
|
popPage.popSample.setChecked(True)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
||||||
|
|
||||||
# Custom Page
|
# Custom Page
|
||||||
@@ -219,7 +202,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
|||||||
customPage.numChapters.setValue(0)
|
customPage.numChapters.setValue(0)
|
||||||
customPage.numScenes.setValue(10)
|
customPage.numScenes.setValue(10)
|
||||||
|
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
|
||||||
|
|
||||||
# Final Page
|
# Final Page
|
||||||
@@ -264,6 +246,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
|
|||||||
nwWiz.reject()
|
nwWiz.reject()
|
||||||
nwWiz.close()
|
nwWiz.close()
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testToolProjectWizard_Run
|
# END Test testToolProjectWizard_Run
|
||||||
|
|||||||
@@ -27,26 +27,16 @@ from mock import causeOSError
|
|||||||
from tools import getGuiItem, writeFile, buildTestProject
|
from tools import getGuiItem, writeFile, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
from PyQt5.QtWidgets import QAction, QFileDialog
|
||||||
|
|
||||||
from novelwriter.tools import GuiWritingStats
|
from novelwriter.tools import GuiWritingStats
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
keyDelay = 2
|
|
||||||
typeDelay = 1
|
|
||||||
stepDelay = 20
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
||||||
"""Test the full writing stats tool.
|
"""Test the full writing stats tool.
|
||||||
"""
|
"""
|
||||||
# Block message box
|
|
||||||
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
|
|
||||||
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
|
|
||||||
|
|
||||||
# Create a project to work on
|
# Create a project to work on
|
||||||
buildTestProject(nwGUI, fncProj)
|
buildTestProject(nwGUI, fncProj)
|
||||||
qtbot.wait(100)
|
qtbot.wait(100)
|
||||||
@@ -60,7 +50,6 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
|
|
||||||
sessLog = getGuiItem("GuiWritingStats")
|
sessLog = getGuiItem("GuiWritingStats")
|
||||||
assert isinstance(sessLog, GuiWritingStats)
|
assert isinstance(sessLog, GuiWritingStats)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# Test Loading
|
# Test Loading
|
||||||
# ============
|
# ============
|
||||||
@@ -186,9 +175,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
|
|
||||||
# No Novel Files
|
# No Novel Files
|
||||||
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert sessLog._saveData(sessLog.FMT_JSON)
|
assert sessLog._saveData(sessLog.FMT_JSON)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
||||||
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
||||||
@@ -234,9 +221,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
# No Note Files
|
# No Note Files
|
||||||
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
|
||||||
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert sessLog._saveData(sessLog.FMT_JSON)
|
assert sessLog._saveData(sessLog.FMT_JSON)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
||||||
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
||||||
@@ -282,11 +267,9 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
# No Negative Entries
|
# No Negative Entries
|
||||||
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
|
||||||
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert sessLog._saveData(sessLog.FMT_JSON)
|
assert sessLog._saveData(sessLog.FMT_JSON)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
||||||
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
||||||
@@ -316,9 +299,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
# Un-hide Zero Entries
|
# Un-hide Zero Entries
|
||||||
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
|
||||||
qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert sessLog._saveData(sessLog.FMT_JSON)
|
assert sessLog._saveData(sessLog.FMT_JSON)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
||||||
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
||||||
@@ -371,9 +352,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
|
|
||||||
# Group by Day
|
# Group by Day
|
||||||
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
|
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
assert sessLog._saveData(sessLog.FMT_JSON)
|
assert sessLog._saveData(sessLog.FMT_JSON)
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
jsonStats = os.path.join(fncDir, "sessionStats.json")
|
||||||
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
|
||||||
@@ -422,10 +401,9 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
assert not sessLog._loadLogFile()
|
assert not sessLog._loadLogFile()
|
||||||
assert not sessLog._saveData(sessLog.FMT_CSV)
|
assert not sessLog._saveData(sessLog.FMT_CSV)
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
sessLog._doClose()
|
sessLog._doClose()
|
||||||
assert nwGUI.closeProject()
|
assert nwGUI.closeProject()
|
||||||
qtbot.wait(stepDelay)
|
|
||||||
|
|
||||||
# END Test testToolWritingStats_Main
|
# END Test testToolWritingStats_Main
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ XML_IGNORE = ("<novelWriterXML", "<saveCount", "<autoCount", "<editTime")
|
|||||||
|
|
||||||
class C:
|
class C:
|
||||||
|
|
||||||
|
# Import and status items from test project when random generator is mocked
|
||||||
|
sNew = "s000000"
|
||||||
|
sNote = "s000001"
|
||||||
|
sDraft = "s000002"
|
||||||
|
sFinished = "s000003"
|
||||||
|
|
||||||
|
iNew = "i000004"
|
||||||
|
iMinor = "i000005"
|
||||||
|
iMajor = "i000006"
|
||||||
|
iMain = "i000007"
|
||||||
|
|
||||||
# Handles from test project when random generator is mocked
|
# Handles from test project when random generator is mocked
|
||||||
hInvalid = "0000000000000"
|
hInvalid = "0000000000000"
|
||||||
hNovelRoot = "0000000000008"
|
hNovelRoot = "0000000000008"
|
||||||
|
|||||||