Add an optional editor toolbar (#1584)
@@ -77,10 +77,10 @@ Text Search Shortcuts
|
|||||||
|
|
||||||
":kbd:`F3`", "Find the next occurrence of the search word"
|
":kbd:`F3`", "Find the next occurrence of the search word"
|
||||||
":kbd:`Ctrl+F`", "Open the search bar and search for the selected word, if any is selected"
|
":kbd:`Ctrl+F`", "Open the search bar and search for the selected word, if any is selected"
|
||||||
":kbd:`Ctrl+G`", "Find next occurrence of search word in current document"
|
":kbd:`Ctrl+G`", "Find the next occurrence of the search word"
|
||||||
":kbd:`Ctrl+H`", "Open the search tool and populate with the selected word (Mac :kbd:`Cmd+=`)"
|
":kbd:`Ctrl+H`", "Open the search tool and populate with the selected word (Mac :kbd:`Cmd+=`)"
|
||||||
":kbd:`Ctrl+Shift+1`", "Replace selected occurrence of the search word, and move to the next"
|
":kbd:`Ctrl+Shift+1`", "Replace selected occurrence of the search word, and move to the next"
|
||||||
":kbd:`Ctrl+Shift+G`", "Find previous occurrence of the search word"
|
":kbd:`Ctrl+Shift+G`", "Find the previous occurrence of the search word"
|
||||||
":kbd:`Shift+F3`", "Find the previous occurrence of the search word"
|
":kbd:`Shift+F3`", "Find the previous occurrence of the search word"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,17 @@ cross = typ_times.svg
|
|||||||
down = typ_chevron-down.svg
|
down = typ_chevron-down.svg
|
||||||
edit = typ_pencil.svg
|
edit = typ_pencil.svg
|
||||||
export = typ_export.svg
|
export = typ_export.svg
|
||||||
|
fmt_bold = nw_tb-bold.svg
|
||||||
|
fmt_italic = nw_tb-italic.svg
|
||||||
|
fmt_mode-md = nw_tb-markdown.svg
|
||||||
|
fmt_mode-sc = nw_tb-shortcode.svg
|
||||||
|
fmt_strike = nw_tb-strike.svg
|
||||||
|
fmt_subscript = nw_tb-subscript.svg
|
||||||
|
fmt_superscript = nw_tb-superscript.svg
|
||||||
|
fmt_underline = nw_tb-underline.svg
|
||||||
forward = typ_chevron-right.svg
|
forward = typ_chevron-right.svg
|
||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-dot-menu.svg
|
||||||
minimise = typ_arrow-minimise.svg
|
minimise = typ_arrow-minimise.svg
|
||||||
noncheckable = mixed_input-none.svg
|
noncheckable = mixed_input-none.svg
|
||||||
proj_chapter = mixed_document-chapter.svg
|
proj_chapter = mixed_document-chapter.svg
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m1.3672 5.5562v-4.7625h1.6289q0.37252 0 0.68662 0.058435 0.32139 0.051131 0.55513 0.18991 0.24104 0.13878 0.37252 0.36522 0.13878 0.22644 0.13878 0.56975 0 0.1607-0.043826 0.32139-0.043826 0.16069-0.12417 0.29948-0.080349 0.13878-0.19721 0.24835-0.10957 0.10957-0.25565 0.1607v0.029218q0.18261 0.043827 0.33601 0.13878 0.1534 0.087653 0.27027 0.22644 0.11687 0.13878 0.18261 0.3287 0.065739 0.18261 0.065739 0.42365 0 0.36522-0.14608 0.62818-0.13878 0.26295-0.39444 0.43826-0.24835 0.168-0.58435 0.25565-0.33601 0.080349-0.72313 0.080349zm1.0737-2.8561h0.50401q0.39444 0 0.56975-0.1534 0.18261-0.1534 0.18261-0.40905 0-0.27757-0.18261-0.39444-0.18261-0.11687-0.56244-0.11687h-0.51131zm0 2.0233h0.60626q0.88383 0 0.88383-0.64279 0-0.31408-0.21914-0.44557-0.21914-0.13878-0.6647-0.13878h-0.60626z" fill="#aeaeae" stroke-opacity=".78039" stroke-width=".54782"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1017 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m2.3084 5.5562 0.94666-4.7625h0.78646l-0.94666 4.7625z" fill="#aeaeae" stroke-opacity=".78039" stroke-width=".54615"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 277 B |
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#aeaeae" stroke-width="1.1">
|
||||||
|
<path d="m0.79397 2.8846-0.29357-0.21175 0.28393-0.5005-0.51975-0.23582 0.11068-0.3465 0.55825 0.1155 0.062562-0.56787h0.37057l0.062562 0.57268 0.55343-0.12032 0.1155 0.3465-0.51975 0.23582 0.28393 0.5005-0.29356 0.21175-0.385-0.42832z"/>
|
||||||
|
<path d="m2.8219 3.15-0.09999 0.37496h0.86866l-0.09374-0.37496q-0.08749-0.30622-0.16873-0.64993-0.081241-0.34372-0.16248-0.66243h-0.024997q-0.074992 0.32496-0.15623 0.66868-0.074992 0.33747-0.16248 0.64369zm-1.4749 2.0623 1.2749-4.0746h1.1062l1.2749 4.0746h-0.97489l-0.24997-0.96865h-1.2437l-0.24997 0.96865z"/>
|
||||||
|
<path d="m4.7812 2.8846-0.29357-0.21175 0.28394-0.5005-0.51975-0.23582 0.11069-0.3465 0.55825 0.1155 0.062562-0.56787h0.37056l0.062562 0.57268 0.55343-0.12032 0.1155 0.3465-0.51975 0.23582 0.28393 0.5005-0.29356 0.21175-0.385-0.42832z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 988 B |
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#aeaeae" stroke-width="1.3799">
|
||||||
|
<path d="m0.52917 5.2683v-4.1866h1.0272v0.37972h-0.49167v3.4272h0.49167v0.37972z"/>
|
||||||
|
<path d="m2.9 2.9219-0.077889 0.29209h0.67667l-0.073022-0.29209q-0.068154-0.23854-0.13144-0.50628-0.063285-0.26775-0.12657-0.51601h-0.019472q-0.058417 0.25314-0.1217 0.52088-0.058417 0.26288-0.12657 0.50141zm-1.1489 1.6065 0.9931-3.174h0.86166l0.9931 3.174h-0.75942l-0.19472-0.75455h-0.96876l-0.19472 0.75455z"/>
|
||||||
|
<path d="m4.7937 5.2683v-0.37972h0.49654v-3.4272h-0.49654v-0.37972h1.0272v4.1866z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 684 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m3.1785 5.5562q-0.46361 0-0.89912-0.17561-0.42848-0.17561-0.75863-0.49873l0.47766-0.55492q0.24585 0.2318 0.56195 0.37931 0.3161 0.14049 0.63219 0.14049 0.39336 0 0.59707-0.16156 0.20371-0.16156 0.20371-0.42848 0-0.14049-0.056194-0.23883-0.04917-0.098341-0.14751-0.16858-0.091316-0.077267-0.22478-0.14049-0.12644-0.063219-0.28097-0.12644l-0.63219-0.27395q-0.16858-0.070243-0.33717-0.17561-0.16156-0.10537-0.29502-0.25288-0.13346-0.14751-0.21775-0.34419-0.077268-0.19668-0.077268-0.44956 0-0.27395 0.11239-0.50575 0.11941-0.23883 0.32312-0.41444 0.20371-0.17561 0.48468-0.27395 0.288-0.098341 0.62517-0.098341 0.40039 0 0.77268 0.16156 0.37229 0.15454 0.63921 0.42146l-0.41444 0.5198q-0.22478-0.18263-0.46361-0.288-0.23883-0.10537-0.53385-0.10537-0.33014 0-0.52683 0.14751-0.19668 0.14049-0.19668 0.39336 0 0.13346 0.056194 0.2318 0.063219 0.091316 0.16156 0.16858 0.10537 0.070244 0.23883 0.13346 0.13346 0.056194 0.27395 0.11239l0.62516 0.2599q0.20371 0.084291 0.37229 0.19668 0.16858 0.11239 0.288 0.2599 0.12644 0.14751 0.19668 0.34419 0.070244 0.18966 0.070244 0.44253 0 0.28097-0.11239 0.52683-0.11239 0.24585-0.33015 0.43551-0.21073 0.18263-0.5198 0.29502-0.30205 0.10536-0.68838 0.10536zm-2.3848-2.6458h4.7625v0.52917h-4.7625z" fill="#aeaeae" stroke-opacity=".78039" stroke-width=".52683"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m2.124 3.0221-0.16749 0.58257h1.2161l-0.16749-0.58257q-0.11651-0.37867-0.22575-0.77919-0.10195-0.40052-0.2039-0.79375h-0.029128q-0.10195 0.40052-0.2039 0.80103-0.10195 0.39323-0.21846 0.7719zm-1.5948 2.5342 1.5511-4.7625h0.99037l1.5511 4.7625h-0.88842l-0.37139-1.2889h-1.6021l-0.37139 1.2889z" fill="#aeaeae" stroke-opacity=".78039" stroke-width=".54616"/>
|
||||||
|
<path d="m4.101 5.5562v-2.3812h0.75354q0.18388 0 0.3353 0.032769 0.15503 0.029128 0.2668 0.098307 0.11538 0.065538 0.17667 0.17841 0.064898 0.11287 0.064898 0.27671 0 0.17113-0.086529 0.31676-0.086529 0.14564-0.24156 0.20026v0.014565q0.19469 0.043691 0.32088 0.18569 0.12979 0.13836 0.12979 0.37867 0 0.17841-0.068503 0.30948-0.068502 0.13107-0.19108 0.21846-0.11899 0.087383-0.28483 0.13107-0.16585 0.04005-0.36054 0.04005zm0.41823-1.3981h0.29925q0.24878 0 0.35693-0.091024 0.11177-0.091025 0.11177-0.24394 0-0.17477-0.11538-0.24759-0.11538-0.072819-0.34612-0.072819h-0.30646zm0 1.0705h0.35334q0.25959 0 0.4002-0.094666 0.14061-0.098307 0.14061-0.29856 0-0.18933-0.13701-0.27308-0.13701-0.087384-0.40381-0.087384h-0.35334z" fill="#69c" stroke-opacity=".78039" stroke-width=".27174"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m2.124 3.0221-0.16749 0.58257h1.2161l-0.16749-0.58257q-0.11651-0.37867-0.22575-0.77919-0.10195-0.40052-0.2039-0.79375h-0.029128q-0.10195 0.40052-0.2039 0.80103-0.10195 0.39323-0.21846 0.7719zm-1.5948 2.5342 1.5511-4.7625h0.99037l1.5511 4.7625h-0.88842l-0.37139-1.2889h-1.6021l-0.37139 1.2889z" fill="#aeaeae" stroke-opacity=".78039" stroke-width=".54616"/>
|
||||||
|
<path d="m4.101 3.175v-2.3812h0.75352q0.18389 0 0.33531 0.032768 0.15503 0.029128 0.2668 0.098307 0.11538 0.065538 0.17667 0.17841 0.064898 0.11287 0.064898 0.27671 0 0.17113-0.086528 0.31676-0.086528 0.14564-0.24156 0.20026v0.014565q0.19469 0.043691 0.32088 0.1857 0.12979 0.13836 0.12979 0.37867 0 0.17841-0.068503 0.30948t-0.19108 0.21845q-0.11897 0.087382-0.28482 0.13107-0.16585 0.04005-0.36054 0.04005zm0.41824-1.3981h0.29924q0.24878 0 0.35693-0.091024 0.11177-0.091024 0.11177-0.24394 0-0.17477-0.11538-0.24759-0.11538-0.072818-0.34612-0.072818h-0.30646zm0 1.0705h0.35332q0.25959 0 0.4002-0.094666 0.14061-0.098307 0.14061-0.29855 0-0.18932-0.13701-0.27307-0.13701-0.087383-0.40381-0.087383h-0.35332z" fill="#69c" stroke-opacity=".78039" stroke-width=".27173"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m3.1782 5.0271q-0.36231 0-0.6547-0.1017-0.28603-0.10806-0.49579-0.33689-0.2034-0.22883-0.31782-0.58479-0.10806-0.36231-0.10806-0.87717v-2.3328h0.73733v2.3899q0 0.33689 0.057206 0.57207 0.063563 0.22883 0.17162 0.36866 0.11441 0.13984 0.26697 0.2034 0.15255 0.057206 0.34324 0.057206 0.19068 0 0.3496-0.057206 0.15891-0.063564 0.27332-0.2034 0.11441-0.13984 0.17798-0.36866 0.063564-0.23519 0.063564-0.57207v-2.3899h0.70555v2.3328q0 0.51487-0.10806 0.87717-0.10806 0.35595-0.31782 0.58479-0.2034 0.22883-0.49579 0.33689-0.28603 0.1017-0.64835 0.1017z" fill="#aeaeae" stroke-opacity=".78039" stroke-width=".47673"/>
|
||||||
|
<rect x="1.0583" y="5.2917" width="4.2333" height=".52917" fill="#aeaeae" stroke-width="1.2172"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 871 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m12 2a2.6315 2.6315 0 0 0-2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314-2.6314 2.6315 2.6315 0 0 0-2.6314-2.6314zm0 7.3686a2.6315 2.6315 0 0 0-2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314-2.6314 2.6315 2.6315 0 0 0-2.6314-2.6314zm0 7.3686a2.6315 2.6315 0 0 0-2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314-2.6314 2.6315 2.6315 0 0 0-2.6314-2.6314z" fill="#aeaeae" stroke-width=".90909"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 629 B |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M19 17h-14c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2zM19 10h-14c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2zM19 3h-14c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2z" fill="#69c"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 397 B |
@@ -46,9 +46,17 @@ cross = typ_times.svg
|
|||||||
down = typ_chevron-down.svg
|
down = typ_chevron-down.svg
|
||||||
edit = typ_pencil.svg
|
edit = typ_pencil.svg
|
||||||
export = typ_export.svg
|
export = typ_export.svg
|
||||||
|
fmt_bold = nw_tb-bold.svg
|
||||||
|
fmt_italic = nw_tb-italic.svg
|
||||||
|
fmt_mode-md = nw_tb-markdown.svg
|
||||||
|
fmt_mode-sc = nw_tb-shortcode.svg
|
||||||
|
fmt_strike = nw_tb-strike.svg
|
||||||
|
fmt_subscript = nw_tb-subscript.svg
|
||||||
|
fmt_superscript = nw_tb-superscript.svg
|
||||||
|
fmt_underline = nw_tb-underline.svg
|
||||||
forward = typ_chevron-right.svg
|
forward = typ_chevron-right.svg
|
||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-dot-menu.svg
|
||||||
minimise = typ_arrow-minimise.svg
|
minimise = typ_arrow-minimise.svg
|
||||||
noncheckable = mixed_input-none.svg
|
noncheckable = mixed_input-none.svg
|
||||||
proj_chapter = mixed_document-chapter.svg
|
proj_chapter = mixed_document-chapter.svg
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m1.3672 5.5562v-4.7625h1.6289q0.37252 0 0.68662 0.058435 0.32139 0.051131 0.55513 0.18991 0.24104 0.13878 0.37252 0.36522 0.13878 0.22644 0.13878 0.56975 0 0.1607-0.043826 0.32139-0.043826 0.16069-0.12417 0.29948-0.080349 0.13878-0.19721 0.24835-0.10957 0.10957-0.25565 0.1607v0.029218q0.18261 0.043827 0.33601 0.13878 0.1534 0.087653 0.27027 0.22644 0.11687 0.13878 0.18261 0.3287 0.065739 0.18261 0.065739 0.42365 0 0.36522-0.14608 0.62818-0.13878 0.26295-0.39444 0.43826-0.24835 0.168-0.58435 0.25565-0.33601 0.080349-0.72313 0.080349zm1.0737-2.8561h0.50401q0.39444 0 0.56975-0.1534 0.18261-0.1534 0.18261-0.40905 0-0.27757-0.18261-0.39444-0.18261-0.11687-0.56244-0.11687h-0.51131zm0 2.0233h0.60626q0.88383 0 0.88383-0.64279 0-0.31408-0.21914-0.44557-0.21914-0.13878-0.6647-0.13878h-0.60626z" fill-opacity=".78039" stroke-opacity=".78039" stroke-width=".54782"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m2.3084 5.5562 0.94666-4.7625h0.78646l-0.94666 4.7625z" fill-opacity=".78039" stroke-opacity=".78039" stroke-width=".54615"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 284 B |
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill-opacity=".78039" stroke-width="1.1">
|
||||||
|
<path d="m0.79397 2.8846-0.29357-0.21175 0.28393-0.5005-0.51975-0.23582 0.11068-0.3465 0.55825 0.1155 0.062562-0.56787h0.37057l0.062562 0.57268 0.55343-0.12032 0.1155 0.3465-0.51975 0.23582 0.28393 0.5005-0.29356 0.21175-0.385-0.42832z"/>
|
||||||
|
<path d="m2.8219 3.15-0.09999 0.37496h0.86866l-0.09374-0.37496q-0.08749-0.30622-0.16873-0.64993-0.081241-0.34372-0.16248-0.66243h-0.024997q-0.074992 0.32496-0.15623 0.66868-0.074992 0.33747-0.16248 0.64369zm-1.4749 2.0623 1.2749-4.0746h1.1062l1.2749 4.0746h-0.97489l-0.24997-0.96865h-1.2437l-0.24997 0.96865z"/>
|
||||||
|
<path d="m4.7812 2.8846-0.29357-0.21175 0.28394-0.5005-0.51975-0.23582 0.11069-0.3465 0.55825 0.1155 0.062562-0.56787h0.37056l0.062562 0.57268 0.55343-0.12032 0.1155 0.3465-0.51975 0.23582 0.28393 0.5005-0.29356 0.21175-0.385-0.42832z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 995 B |
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill-opacity=".78039" stroke-width="1.3799">
|
||||||
|
<path d="m0.52917 5.2683v-4.1866h1.0272v0.37972h-0.49167v3.4272h0.49167v0.37972z"/>
|
||||||
|
<path d="m2.9 2.9219-0.077889 0.29209h0.67667l-0.073022-0.29209q-0.068154-0.23854-0.13144-0.50628-0.063285-0.26775-0.12657-0.51601h-0.019472q-0.058417 0.25314-0.1217 0.52088-0.058417 0.26288-0.12657 0.50141zm-1.1489 1.6065 0.9931-3.174h0.86166l0.9931 3.174h-0.75942l-0.19472-0.75455h-0.96876l-0.19472 0.75455z"/>
|
||||||
|
<path d="m4.7937 5.2683v-0.37972h0.49654v-3.4272h-0.49654v-0.37972h1.0272v4.1866z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 691 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m3.1785 5.5562q-0.46361 0-0.89912-0.17561-0.42848-0.17561-0.75863-0.49873l0.47766-0.55492q0.24585 0.2318 0.56195 0.37931 0.3161 0.14049 0.63219 0.14049 0.39336 0 0.59707-0.16156 0.20371-0.16156 0.20371-0.42848 0-0.14049-0.056194-0.23883-0.04917-0.098341-0.14751-0.16858-0.091316-0.077267-0.22478-0.14049-0.12644-0.063219-0.28097-0.12644l-0.63219-0.27395q-0.16858-0.070243-0.33717-0.17561-0.16156-0.10537-0.29502-0.25288-0.13346-0.14751-0.21775-0.34419-0.077268-0.19668-0.077268-0.44956 0-0.27395 0.11239-0.50575 0.11941-0.23883 0.32312-0.41444 0.20371-0.17561 0.48468-0.27395 0.288-0.098341 0.62517-0.098341 0.40039 0 0.77268 0.16156 0.37229 0.15454 0.63921 0.42146l-0.41444 0.5198q-0.22478-0.18263-0.46361-0.288-0.23883-0.10537-0.53385-0.10537-0.33014 0-0.52683 0.14751-0.19668 0.14049-0.19668 0.39336 0 0.13346 0.056194 0.2318 0.063219 0.091316 0.16156 0.16858 0.10537 0.070244 0.23883 0.13346 0.13346 0.056194 0.27395 0.11239l0.62516 0.2599q0.20371 0.084291 0.37229 0.19668 0.16858 0.11239 0.288 0.2599 0.12644 0.14751 0.19668 0.34419 0.070244 0.18966 0.070244 0.44253 0 0.28097-0.11239 0.52683-0.11239 0.24585-0.33015 0.43551-0.21073 0.18263-0.5198 0.29502-0.30205 0.10536-0.68838 0.10536zm-2.3848-2.6458h4.7625v0.52917h-4.7625z" fill-opacity=".78039" stroke-opacity=".78039" stroke-width=".52683"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m2.124 3.0221-0.16749 0.58257h1.2161l-0.16749-0.58257q-0.11651-0.37867-0.22575-0.77919-0.10195-0.40052-0.2039-0.79375h-0.029128q-0.10195 0.40052-0.2039 0.80103-0.10195 0.39323-0.21846 0.7719zm-1.5948 2.5342 1.5511-4.7625h0.99037l1.5511 4.7625h-0.88842l-0.37139-1.2889h-1.6021l-0.37139 1.2889z" fill-opacity=".78039" stroke-opacity=".78039" stroke-width=".54616"/>
|
||||||
|
<path d="m4.101 5.5562v-2.3812h0.75354q0.18388 0 0.3353 0.032769 0.15503 0.029128 0.2668 0.098307 0.11538 0.065538 0.17667 0.17841 0.064898 0.11287 0.064898 0.27671 0 0.17113-0.086529 0.31676-0.086529 0.14564-0.24156 0.20026v0.014565q0.19469 0.043691 0.32088 0.18569 0.12979 0.13836 0.12979 0.37867 0 0.17841-0.068503 0.30948-0.068502 0.13107-0.19108 0.21846-0.11899 0.087383-0.28483 0.13107-0.16585 0.04005-0.36054 0.04005zm0.41823-1.3981h0.29925q0.24878 0 0.35693-0.091024 0.11177-0.091025 0.11177-0.24394 0-0.17477-0.11538-0.24759-0.11538-0.072819-0.34612-0.072819h-0.30646zm0 1.0705h0.35334q0.25959 0 0.4002-0.094666 0.14061-0.098307 0.14061-0.29856 0-0.18933-0.13701-0.27308-0.13701-0.087384-0.40381-0.087384h-0.35334z" fill="#4271ae" stroke-opacity=".78039" stroke-width=".27174"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m2.124 3.0221-0.16749 0.58257h1.2161l-0.16749-0.58257q-0.11651-0.37867-0.22575-0.77919-0.10195-0.40052-0.2039-0.79375h-0.029128q-0.10195 0.40052-0.2039 0.80103-0.10195 0.39323-0.21846 0.7719zm-1.5948 2.5342 1.5511-4.7625h0.99037l1.5511 4.7625h-0.88842l-0.37139-1.2889h-1.6021l-0.37139 1.2889z" fill-opacity=".78039" stroke-opacity=".78039" stroke-width=".54616"/>
|
||||||
|
<path d="m4.101 3.175v-2.3812h0.75352q0.18389 0 0.33531 0.032768 0.15503 0.029128 0.2668 0.098307 0.11538 0.065538 0.17667 0.17841 0.064898 0.11287 0.064898 0.27671 0 0.17113-0.086528 0.31676-0.086528 0.14564-0.24156 0.20026v0.014565q0.19469 0.043691 0.32088 0.1857 0.12979 0.13836 0.12979 0.37867 0 0.17841-0.068503 0.30948t-0.19108 0.21845q-0.11897 0.087382-0.28482 0.13107-0.16585 0.04005-0.36054 0.04005zm0.41824-1.3981h0.29924q0.24878 0 0.35693-0.091024 0.11177-0.091024 0.11177-0.24394 0-0.17477-0.11538-0.24759-0.11538-0.072818-0.34612-0.072818h-0.30646zm0 1.0705h0.35332q0.25959 0 0.4002-0.094666 0.14061-0.098307 0.14061-0.29855 0-0.18932-0.13701-0.27307-0.13701-0.087383-0.40381-0.087383h-0.35332z" fill="#4271ae" stroke-opacity=".78039" stroke-width=".27173"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m3.1782 5.0271q-0.36231 0-0.6547-0.1017-0.28603-0.10806-0.49579-0.33689-0.2034-0.22883-0.31782-0.58479-0.10806-0.36231-0.10806-0.87717v-2.3328h0.73733v2.3899q0 0.33689 0.057206 0.57207 0.063563 0.22883 0.17162 0.36866 0.11441 0.13984 0.26697 0.2034 0.15255 0.057206 0.34324 0.057206 0.19068 0 0.3496-0.057206 0.15891-0.063564 0.27332-0.2034 0.11441-0.13984 0.17798-0.36866 0.063564-0.23519 0.063564-0.57207v-2.3899h0.70555v2.3328q0 0.51487-0.10806 0.87717-0.10806 0.35595-0.31782 0.58479-0.2034 0.22883-0.49579 0.33689-0.28603 0.1017-0.64835 0.1017z" fill-opacity=".78039" stroke-opacity=".78039" stroke-width=".47673"/>
|
||||||
|
<rect x="1.0583" y="5.2917" width="4.2333" height=".52917" fill-opacity=".78039" stroke-width="1.2172"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 885 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m12 2a2.6315 2.6315 0 0 0-2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314-2.6314 2.6315 2.6315 0 0 0-2.6314-2.6314zm0 7.3686a2.6315 2.6315 0 0 0-2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314-2.6314 2.6315 2.6315 0 0 0-2.6314-2.6314zm0 7.3686a2.6315 2.6315 0 0 0-2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314 2.6314 2.6315 2.6315 0 0 0 2.6314-2.6314 2.6315 2.6315 0 0 0-2.6314-2.6314z" fill-opacity=".78039" stroke-width=".90909"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 636 B |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M19 17h-14c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2zM19 10h-14c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2zM19 3h-14c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2z" fill="#4271ae"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 400 B |
@@ -178,9 +178,11 @@ class Config:
|
|||||||
self.spellLanguage = "en"
|
self.spellLanguage = "en"
|
||||||
|
|
||||||
# State
|
# State
|
||||||
self.showRefPanel = True # The reference panel for the viewer is visible
|
self.showRefPanel = True # The reference panel for the viewer is visible
|
||||||
self.viewComments = True # Comments are shown in the viewer
|
self.showEditToolBar = False # The document editor toolbar visibility
|
||||||
self.viewSynopsis = True # Synopsis is shown in the viewer
|
self.useShortcodes = False # Use shorcodes for basic formatting
|
||||||
|
self.viewComments = True # Comments are shown in the viewer
|
||||||
|
self.viewSynopsis = True # Synopsis is shown in the viewer
|
||||||
|
|
||||||
# Search Bar Switches
|
# Search Bar Switches
|
||||||
self.searchCase = False
|
self.searchCase = False
|
||||||
@@ -598,15 +600,17 @@ class Config:
|
|||||||
|
|
||||||
# State
|
# State
|
||||||
sec = "State"
|
sec = "State"
|
||||||
self.showRefPanel = conf.rdBool(sec, "showrefpanel", self.showRefPanel)
|
self.showRefPanel = conf.rdBool(sec, "showrefpanel", self.showRefPanel)
|
||||||
self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
|
self.showEditToolBar = conf.rdBool(sec, "showedittoolbar", self.showEditToolBar)
|
||||||
self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
|
self.useShortcodes = conf.rdBool(sec, "useshortcodes", self.useShortcodes)
|
||||||
self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
|
self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
|
||||||
self.searchWord = conf.rdBool(sec, "searchword", self.searchWord)
|
self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
|
||||||
self.searchRegEx = conf.rdBool(sec, "searchregex", self.searchRegEx)
|
self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
|
||||||
self.searchLoop = conf.rdBool(sec, "searchloop", self.searchLoop)
|
self.searchWord = conf.rdBool(sec, "searchword", self.searchWord)
|
||||||
self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile)
|
self.searchRegEx = conf.rdBool(sec, "searchregex", self.searchRegEx)
|
||||||
self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap)
|
self.searchLoop = conf.rdBool(sec, "searchloop", self.searchLoop)
|
||||||
|
self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile)
|
||||||
|
self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap)
|
||||||
|
|
||||||
# Deprecated Settings or Locations as of 2.0
|
# Deprecated Settings or Locations as of 2.0
|
||||||
# ToDo: These will be loaded for a few minor releases until the users have converted them
|
# ToDo: These will be loaded for a few minor releases until the users have converted them
|
||||||
@@ -721,6 +725,8 @@ class Config:
|
|||||||
|
|
||||||
conf["State"] = {
|
conf["State"] = {
|
||||||
"showrefpanel": str(self.showRefPanel),
|
"showrefpanel": str(self.showRefPanel),
|
||||||
|
"showedittoolbar": str(self.showEditToolBar),
|
||||||
|
"useshortcodes": str(self.useShortcodes),
|
||||||
"viewcomments": str(self.viewComments),
|
"viewcomments": str(self.viewComments),
|
||||||
"viewsynopsis": str(self.viewSynopsis),
|
"viewsynopsis": str(self.viewSynopsis),
|
||||||
"searchcase": str(self.searchCase),
|
"searchcase": str(self.searchCase),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any, TypeVar
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
@@ -40,6 +40,8 @@ if TYPE_CHECKING: # pragma: no cover
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
NWEnum = TypeVar("NWEnum", bound=Enum)
|
||||||
|
|
||||||
VALID_MAP = {
|
VALID_MAP = {
|
||||||
"GuiWritingStats": {
|
"GuiWritingStats": {
|
||||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||||
@@ -201,11 +203,11 @@ class OptionState:
|
|||||||
return checkBool(self._state[group].get(name, default), default)
|
return checkBool(self._state[group].get(name, default), default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getEnum(self, group: str, name: str, lookup: type, default: Enum) -> Enum:
|
def getEnum(self, group: str, name: str, lookup: type, default: NWEnum) -> NWEnum:
|
||||||
"""Return the value mapped to an enum. Otherwise return the
|
"""Return the value mapped to an enum. Otherwise return the
|
||||||
default value.
|
default value.
|
||||||
"""
|
"""
|
||||||
if issubclass(lookup, Enum):
|
if issubclass(lookup, type(default)):
|
||||||
if group in self._state:
|
if group in self._state:
|
||||||
if name in self._state[group]:
|
if name in self._state[group]:
|
||||||
value = self._state[group][name]
|
value = self._state[group][name]
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ from PyQt5.QtGui import (
|
|||||||
)
|
)
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAction, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
|
QAction, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
|
||||||
QPlainTextEdit, QPushButton, QShortcut, QToolBar, QToolButton, QWidget,
|
QPlainTextEdit, QPushButton, QShortcut, QToolBar, QToolButton, QVBoxLayout, QWidget,
|
||||||
qApp
|
qApp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,6 +83,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
novelStructureChanged = pyqtSignal()
|
novelStructureChanged = pyqtSignal()
|
||||||
novelItemMetaChanged = pyqtSignal(str)
|
novelItemMetaChanged = pyqtSignal(str)
|
||||||
spellCheckStateChanged = pyqtSignal(bool)
|
spellCheckStateChanged = pyqtSignal(bool)
|
||||||
|
closeDocumentRequest = pyqtSignal()
|
||||||
|
toggleFocusModeRequest = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, mainGui: GuiMain) -> None:
|
def __init__(self, mainGui: GuiMain) -> None:
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
@@ -135,6 +137,12 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self.docHeader = GuiDocEditHeader(self)
|
self.docHeader = GuiDocEditHeader(self)
|
||||||
self.docFooter = GuiDocEditFooter(self)
|
self.docFooter = GuiDocEditFooter(self)
|
||||||
self.docSearch = GuiDocEditSearch(self)
|
self.docSearch = GuiDocEditSearch(self)
|
||||||
|
self.docToolBar = GuiDocToolBar(self)
|
||||||
|
|
||||||
|
# Connect Signals
|
||||||
|
self.docHeader.closeDocumentRequest.connect(self._closeCurrentDocument)
|
||||||
|
self.docHeader.toggleToolBarRequest.connect(self._toggleToolBarVisibility)
|
||||||
|
self.docToolBar.requestDocAction.connect(self.docAction)
|
||||||
|
|
||||||
# Context Menu
|
# Context Menu
|
||||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
@@ -248,6 +256,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self.docSearch.updateTheme()
|
self.docSearch.updateTheme()
|
||||||
self.docHeader.updateTheme()
|
self.docHeader.updateTheme()
|
||||||
self.docFooter.updateTheme()
|
self.docFooter.updateTheme()
|
||||||
|
self.docToolBar.updateTheme()
|
||||||
return
|
return
|
||||||
|
|
||||||
def updateSyntaxColours(self) -> None:
|
def updateSyntaxColours(self) -> None:
|
||||||
@@ -400,6 +409,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
qApp.processEvents()
|
qApp.processEvents()
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
self._qDocument.clearUndoRedoStacks()
|
self._qDocument.clearUndoRedoStacks()
|
||||||
|
self.docToolBar.setVisible(CONFIG.showEditToolBar)
|
||||||
|
|
||||||
qApp.restoreOverrideCursor()
|
qApp.restoreOverrideCursor()
|
||||||
|
|
||||||
@@ -514,6 +524,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
fY = wH - fH - tB - sH
|
fY = wH - fH - tB - sH
|
||||||
self.docHeader.setGeometry(tB, tB, tW, tH)
|
self.docHeader.setGeometry(tB, tB, tW, tH)
|
||||||
self.docFooter.setGeometry(tB, fY, tW, fH)
|
self.docFooter.setGeometry(tB, fY, tW, fH)
|
||||||
|
self.docToolBar.move(0, tH)
|
||||||
|
|
||||||
rH = 0
|
rH = 0
|
||||||
if self.docSearch.isVisible():
|
if self.docSearch.isVisible():
|
||||||
@@ -841,30 +852,11 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def insertKeyWord(self, keyword: str) -> bool:
|
|
||||||
"""Insert a keyword in the text editor, at the cursor position.
|
|
||||||
If the insert line is not blank, a new line is started.
|
|
||||||
"""
|
|
||||||
if keyword not in nwKeyWords.VALID_KEYS:
|
|
||||||
logger.error("Invalid keyword '%s'", keyword)
|
|
||||||
return False
|
|
||||||
logger.debug("Inserting keyword '%s'", keyword)
|
|
||||||
state = self.insertNewBlock("%s: " % keyword)
|
|
||||||
return state
|
|
||||||
|
|
||||||
def closeSearch(self) -> bool:
|
def closeSearch(self) -> bool:
|
||||||
"""Close the search box."""
|
"""Close the search box."""
|
||||||
self.docSearch.closeSearch()
|
self.docSearch.closeSearch()
|
||||||
return self.docSearch.isVisible()
|
return self.docSearch.isVisible()
|
||||||
|
|
||||||
def toggleSearch(self) -> None:
|
|
||||||
"""Toggle the visibility of the search box."""
|
|
||||||
if self.docSearch.isVisible():
|
|
||||||
self.docSearch.closeSearch()
|
|
||||||
else:
|
|
||||||
self.beginSearch()
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Document Events and Maintenance
|
# Document Events and Maintenance
|
||||||
##
|
##
|
||||||
@@ -959,6 +951,27 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self.updateDocMargins()
|
self.updateDocMargins()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def insertKeyWord(self, keyword: str) -> bool:
|
||||||
|
"""Insert a keyword in the text editor, at the cursor position.
|
||||||
|
If the insert line is not blank, a new line is started.
|
||||||
|
"""
|
||||||
|
if keyword not in nwKeyWords.VALID_KEYS:
|
||||||
|
logger.error("Invalid keyword '%s'", keyword)
|
||||||
|
return False
|
||||||
|
logger.debug("Inserting keyword '%s'", keyword)
|
||||||
|
state = self.insertNewBlock("%s: " % keyword)
|
||||||
|
return state
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def toggleSearch(self) -> None:
|
||||||
|
"""Toggle the visibility of the search box."""
|
||||||
|
if self.docSearch.isVisible():
|
||||||
|
self.docSearch.closeSearch()
|
||||||
|
else:
|
||||||
|
self.beginSearch()
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Private Slots
|
# Private Slots
|
||||||
##
|
##
|
||||||
@@ -1179,6 +1192,21 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _closeCurrentDocument(self) -> None:
|
||||||
|
"""Close the document. Forwarded to the main Gui."""
|
||||||
|
self.closeDocumentRequest.emit()
|
||||||
|
self.docToolBar.setVisible(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _toggleToolBarVisibility(self) -> None:
|
||||||
|
"""Toggle the visibility of the tool bar."""
|
||||||
|
state = not self.docToolBar.isVisible()
|
||||||
|
self.docToolBar.setVisible(state)
|
||||||
|
CONFIG.showEditToolBar = state
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Search & Replace
|
# Search & Replace
|
||||||
##
|
##
|
||||||
@@ -2072,6 +2100,142 @@ class BackgroundWordCounterSignals(QObject):
|
|||||||
# END Class BackgroundWordCounterSignals
|
# END Class BackgroundWordCounterSignals
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================================== #
|
||||||
|
# The Formatting and Options Fold Out Menu
|
||||||
|
# Only used by DocEditor, and is opened by the first button in the header
|
||||||
|
# =============================================================================================== #
|
||||||
|
|
||||||
|
class GuiDocToolBar(QWidget):
|
||||||
|
|
||||||
|
requestDocAction = pyqtSignal(nwDocAction)
|
||||||
|
|
||||||
|
def __init__(self, docEditor: GuiDocEditor) -> None:
|
||||||
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
|
logger.debug("Create: GuiDocToolBar")
|
||||||
|
|
||||||
|
cM = CONFIG.pxInt(4)
|
||||||
|
tPx = int(0.8*SHARED.theme.fontPixelSize)
|
||||||
|
iconSize = QSize(tPx, tPx)
|
||||||
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# General Buttons
|
||||||
|
# ===============
|
||||||
|
|
||||||
|
self.tbMode = QToolButton(self)
|
||||||
|
self.tbMode.setToolTip(self.tr("Toggle Markdown or Shortcodes Mode"))
|
||||||
|
self.tbMode.setIconSize(iconSize)
|
||||||
|
self.tbMode.setCheckable(True)
|
||||||
|
self.tbMode.setChecked(CONFIG.useShortcodes)
|
||||||
|
self.tbMode.toggled.connect(self._toggleFormatMode)
|
||||||
|
|
||||||
|
self.tbBold = QToolButton(self)
|
||||||
|
self.tbBold.setIconSize(iconSize)
|
||||||
|
self.tbBold.clicked.connect(self._formatBold)
|
||||||
|
|
||||||
|
self.tbItalic = QToolButton(self)
|
||||||
|
self.tbItalic.setIconSize(iconSize)
|
||||||
|
self.tbItalic.clicked.connect(self._formatItalic)
|
||||||
|
|
||||||
|
self.tbStrike = QToolButton(self)
|
||||||
|
self.tbStrike.setIconSize(iconSize)
|
||||||
|
self.tbStrike.clicked.connect(self._formatStrike)
|
||||||
|
|
||||||
|
self.tbUnderline = QToolButton(self)
|
||||||
|
self.tbUnderline.setIconSize(iconSize)
|
||||||
|
self.tbUnderline.clicked.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_ULINE)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tbSuperscript = QToolButton(self)
|
||||||
|
self.tbSuperscript.setIconSize(iconSize)
|
||||||
|
self.tbSuperscript.clicked.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_SUP)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tbSubscript = QToolButton(self)
|
||||||
|
self.tbSubscript.setIconSize(iconSize)
|
||||||
|
self.tbSubscript.clicked.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_SUB)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assemble
|
||||||
|
# ========
|
||||||
|
|
||||||
|
self.outerBox = QVBoxLayout()
|
||||||
|
self.outerBox.addWidget(self.tbMode)
|
||||||
|
self.outerBox.addWidget(self.tbBold)
|
||||||
|
self.outerBox.addWidget(self.tbItalic)
|
||||||
|
self.outerBox.addWidget(self.tbStrike)
|
||||||
|
self.outerBox.addWidget(self.tbUnderline)
|
||||||
|
self.outerBox.addWidget(self.tbSuperscript)
|
||||||
|
self.outerBox.addWidget(self.tbSubscript)
|
||||||
|
self.outerBox.setContentsMargins(cM, cM, cM, cM)
|
||||||
|
self.outerBox.setSpacing(cM)
|
||||||
|
|
||||||
|
self.setLayout(self.outerBox)
|
||||||
|
self.updateTheme()
|
||||||
|
|
||||||
|
logger.debug("Ready: GuiDocToolBar")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def updateTheme(self) -> None:
|
||||||
|
"""Initialise GUI elements that depend on specific settings."""
|
||||||
|
palette = QPalette()
|
||||||
|
palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack))
|
||||||
|
palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText))
|
||||||
|
palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
|
||||||
|
self.setPalette(palette)
|
||||||
|
|
||||||
|
tPx = int(0.8*SHARED.theme.fontPixelSize)
|
||||||
|
self.tbMode.setIcon(SHARED.theme.getToggleIcon("fmt_mode", (tPx, tPx)))
|
||||||
|
self.tbBold.setIcon(SHARED.theme.getIcon("fmt_bold"))
|
||||||
|
self.tbItalic.setIcon(SHARED.theme.getIcon("fmt_italic"))
|
||||||
|
self.tbStrike.setIcon(SHARED.theme.getIcon("fmt_strike"))
|
||||||
|
self.tbUnderline.setIcon(SHARED.theme.getIcon("fmt_underline"))
|
||||||
|
self.tbSuperscript.setIcon(SHARED.theme.getIcon("fmt_superscript"))
|
||||||
|
self.tbSubscript.setIcon(SHARED.theme.getIcon("fmt_subscript"))
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Private Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
|
def _toggleFormatMode(self, checked: bool) -> None:
|
||||||
|
"""Toggle the formatting mode."""
|
||||||
|
CONFIG.useShortcodes = checked
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _formatBold(self):
|
||||||
|
"""Call the bold format action."""
|
||||||
|
self.requestDocAction.emit(
|
||||||
|
nwDocAction.SC_BOLD if self.tbMode.isChecked() else nwDocAction.STRONG
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _formatItalic(self):
|
||||||
|
"""Call the italic format action."""
|
||||||
|
self.requestDocAction.emit(
|
||||||
|
nwDocAction.SC_ITALIC if self.tbMode.isChecked() else nwDocAction.EMPH
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _formatStrike(self):
|
||||||
|
"""Call the strikethrough format action."""
|
||||||
|
self.requestDocAction.emit(
|
||||||
|
nwDocAction.SC_STRIKE if self.tbMode.isChecked() else nwDocAction.STRIKE
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class GuiDocToolBar
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
# The Embedded Document Search/Replace Feature
|
# The Embedded Document Search/Replace Feature
|
||||||
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
|
# Only used by DocEditor, and is at a fixed position in the QTextEdit's viewport
|
||||||
@@ -2487,6 +2651,9 @@ class GuiDocEditSearch(QFrame):
|
|||||||
|
|
||||||
class GuiDocEditHeader(QWidget):
|
class GuiDocEditHeader(QWidget):
|
||||||
|
|
||||||
|
closeDocumentRequest = pyqtSignal()
|
||||||
|
toggleToolBarRequest = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, docEditor: GuiDocEditor) -> None:
|
def __init__(self, docEditor: GuiDocEditor) -> None:
|
||||||
super().__init__(parent=docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
@@ -2499,6 +2666,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
|
|
||||||
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
||||||
hSp = CONFIG.pxInt(6)
|
hSp = CONFIG.pxInt(6)
|
||||||
|
iconSize = QSize(fPx, fPx)
|
||||||
|
|
||||||
# Main Widget Settings
|
# Main Widget Settings
|
||||||
self.setAutoFillBackground(True)
|
self.setAutoFillBackground(True)
|
||||||
@@ -2518,46 +2686,46 @@ class GuiDocEditHeader(QWidget):
|
|||||||
self.itemTitle.setFont(lblFont)
|
self.itemTitle.setFont(lblFont)
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.editButton = QToolButton(self)
|
self.tbButton = QToolButton(self)
|
||||||
self.editButton.setContentsMargins(0, 0, 0, 0)
|
self.tbButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.editButton.setIconSize(QSize(fPx, fPx))
|
self.tbButton.setIconSize(iconSize)
|
||||||
self.editButton.setFixedSize(fPx, fPx)
|
self.tbButton.setFixedSize(fPx, fPx)
|
||||||
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.tbButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.editButton.setVisible(False)
|
self.tbButton.setVisible(False)
|
||||||
self.editButton.setToolTip(self.tr("Edit document label"))
|
self.tbButton.setToolTip(self.tr("Toggle Tool Bar"))
|
||||||
self.editButton.clicked.connect(self._editDocument)
|
self.tbButton.clicked.connect(lambda: self.toggleToolBarRequest.emit())
|
||||||
|
|
||||||
self.searchButton = QToolButton(self)
|
self.searchButton = QToolButton(self)
|
||||||
self.searchButton.setContentsMargins(0, 0, 0, 0)
|
self.searchButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.searchButton.setIconSize(QSize(fPx, fPx))
|
self.searchButton.setIconSize(iconSize)
|
||||||
self.searchButton.setFixedSize(fPx, fPx)
|
self.searchButton.setFixedSize(fPx, fPx)
|
||||||
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"))
|
||||||
self.searchButton.clicked.connect(self._searchDocument)
|
self.searchButton.clicked.connect(self.docEditor.toggleSearch)
|
||||||
|
|
||||||
self.minmaxButton = QToolButton(self)
|
self.minmaxButton = QToolButton(self)
|
||||||
self.minmaxButton.setContentsMargins(0, 0, 0, 0)
|
self.minmaxButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.minmaxButton.setIconSize(QSize(fPx, fPx))
|
self.minmaxButton.setIconSize(iconSize)
|
||||||
self.minmaxButton.setFixedSize(fPx, fPx)
|
self.minmaxButton.setFixedSize(fPx, fPx)
|
||||||
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(lambda: self.docEditor.toggleFocusModeRequest.emit())
|
||||||
|
|
||||||
self.closeButton = QToolButton(self)
|
self.closeButton = QToolButton(self)
|
||||||
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
self.closeButton.setContentsMargins(0, 0, 0, 0)
|
||||||
self.closeButton.setIconSize(QSize(fPx, fPx))
|
self.closeButton.setIconSize(iconSize)
|
||||||
self.closeButton.setFixedSize(fPx, fPx)
|
self.closeButton.setFixedSize(fPx, fPx)
|
||||||
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"))
|
||||||
self.closeButton.clicked.connect(self._closeDocument)
|
self.closeButton.clicked.connect(self._closeDocument)
|
||||||
|
|
||||||
# Assemble Layout
|
# Assemble Layout
|
||||||
self.outerBox = QHBoxLayout()
|
self.outerBox = QHBoxLayout()
|
||||||
self.outerBox.setSpacing(hSp)
|
self.outerBox.setSpacing(hSp)
|
||||||
self.outerBox.addWidget(self.editButton, 0)
|
self.outerBox.addWidget(self.tbButton, 0)
|
||||||
self.outerBox.addWidget(self.searchButton, 0)
|
self.outerBox.addWidget(self.searchButton, 0)
|
||||||
self.outerBox.addWidget(self.itemTitle, 1)
|
self.outerBox.addWidget(self.itemTitle, 1)
|
||||||
self.outerBox.addWidget(self.minmaxButton, 0)
|
self.outerBox.addWidget(self.minmaxButton, 0)
|
||||||
@@ -2583,7 +2751,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
|
|
||||||
def updateTheme(self) -> None:
|
def updateTheme(self) -> None:
|
||||||
"""Update theme elements."""
|
"""Update theme elements."""
|
||||||
self.editButton.setIcon(SHARED.theme.getIcon("edit"))
|
self.tbButton.setIcon(SHARED.theme.getIcon("menu"))
|
||||||
self.searchButton.setIcon(SHARED.theme.getIcon("search"))
|
self.searchButton.setIcon(SHARED.theme.getIcon("search"))
|
||||||
self.minmaxButton.setIcon(SHARED.theme.getIcon("maximise"))
|
self.minmaxButton.setIcon(SHARED.theme.getIcon("maximise"))
|
||||||
self.closeButton.setIcon(SHARED.theme.getIcon("close"))
|
self.closeButton.setIcon(SHARED.theme.getIcon("close"))
|
||||||
@@ -2593,7 +2761,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||||
).format(*SHARED.theme.colText)
|
).format(*SHARED.theme.colText)
|
||||||
|
|
||||||
self.editButton.setStyleSheet(buttonStyle)
|
self.tbButton.setStyleSheet(buttonStyle)
|
||||||
self.searchButton.setStyleSheet(buttonStyle)
|
self.searchButton.setStyleSheet(buttonStyle)
|
||||||
self.minmaxButton.setStyleSheet(buttonStyle)
|
self.minmaxButton.setStyleSheet(buttonStyle)
|
||||||
self.closeButton.setStyleSheet(buttonStyle)
|
self.closeButton.setStyleSheet(buttonStyle)
|
||||||
@@ -2623,7 +2791,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
self._docHandle = tHandle
|
self._docHandle = tHandle
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
self.itemTitle.setText("")
|
self.itemTitle.setText("")
|
||||||
self.editButton.setVisible(False)
|
self.tbButton.setVisible(False)
|
||||||
self.searchButton.setVisible(False)
|
self.searchButton.setVisible(False)
|
||||||
self.closeButton.setVisible(False)
|
self.closeButton.setVisible(False)
|
||||||
self.minmaxButton.setVisible(False)
|
self.minmaxButton.setVisible(False)
|
||||||
@@ -2645,7 +2813,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
return False
|
return False
|
||||||
self.itemTitle.setText(nwItem.itemName)
|
self.itemTitle.setText(nwItem.itemName)
|
||||||
|
|
||||||
self.editButton.setVisible(True)
|
self.tbButton.setVisible(True)
|
||||||
self.searchButton.setVisible(True)
|
self.searchButton.setVisible(True)
|
||||||
self.closeButton.setVisible(True)
|
self.closeButton.setVisible(True)
|
||||||
self.minmaxButton.setVisible(True)
|
self.minmaxButton.setVisible(True)
|
||||||
@@ -2667,34 +2835,16 @@ class GuiDocEditHeader(QWidget):
|
|||||||
# Private Slots
|
# Private Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _editDocument(self) -> None:
|
|
||||||
"""Open the edit item dialog from the main GUI."""
|
|
||||||
self.mainGui.editItemLabel(self._docHandle)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _searchDocument(self) -> None:
|
|
||||||
"""Toggle the visibility of the search box."""
|
|
||||||
self.docEditor.toggleSearch()
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _closeDocument(self) -> None:
|
def _closeDocument(self) -> None:
|
||||||
"""Trigger the close editor on the main window."""
|
"""Trigger the close editor on the main window."""
|
||||||
self.mainGui.closeDocEditor()
|
self.closeDocumentRequest.emit()
|
||||||
self.editButton.setVisible(False)
|
self.tbButton.setVisible(False)
|
||||||
self.searchButton.setVisible(False)
|
self.searchButton.setVisible(False)
|
||||||
self.closeButton.setVisible(False)
|
self.closeButton.setVisible(False)
|
||||||
self.minmaxButton.setVisible(False)
|
self.minmaxButton.setVisible(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _minmaxDocument(self) -> None:
|
|
||||||
"""Switch on or off Focus Mode."""
|
|
||||||
self.mainGui.toggleFocusMode()
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Events
|
# Events
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, QSize, Qt, QUrl
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, QSize, Qt, QUrl
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QColor, QCursor, QFont, QIcon, QMouseEvent, QPalette, QResizeEvent,
|
QColor, QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
|
||||||
QTextCursor, QTextOption
|
QTextOption
|
||||||
)
|
)
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAction, qApp, QFrame, QHBoxLayout, QLabel, QMenu, QScrollArea,
|
QAction, qApp, QFrame, QHBoxLayout, QLabel, QMenu, QScrollArea,
|
||||||
@@ -705,7 +705,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
self.backButton.setFixedSize(fPx, fPx)
|
self.backButton.setFixedSize(fPx, fPx)
|
||||||
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)
|
||||||
@@ -714,7 +714,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
self.forwardButton.setFixedSize(fPx, fPx)
|
self.forwardButton.setFixedSize(fPx, fPx)
|
||||||
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)
|
||||||
@@ -723,7 +723,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
self.refreshButton.setFixedSize(fPx, fPx)
|
self.refreshButton.setFixedSize(fPx, fPx)
|
||||||
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"))
|
||||||
self.refreshButton.clicked.connect(self._refreshDocument)
|
self.refreshButton.clicked.connect(self._refreshDocument)
|
||||||
|
|
||||||
self.closeButton = QToolButton(self)
|
self.closeButton = QToolButton(self)
|
||||||
@@ -732,7 +732,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
self.closeButton.setFixedSize(fPx, fPx)
|
self.closeButton.setFixedSize(fPx, fPx)
|
||||||
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"))
|
||||||
self.closeButton.clicked.connect(self._closeDocument)
|
self.closeButton.clicked.connect(self._closeDocument)
|
||||||
|
|
||||||
# Assemble Layout
|
# Assemble Layout
|
||||||
@@ -1020,24 +1020,12 @@ class GuiDocViewFooter(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateTheme(self):
|
def updateTheme(self) -> None:
|
||||||
"""Update theme elements.
|
"""Update theme elements."""
|
||||||
"""
|
|
||||||
# Icons
|
# Icons
|
||||||
|
|
||||||
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
||||||
|
stickyIcon = SHARED.theme.getToggleIcon("sticky", (fPx, fPx))
|
||||||
stickyOn = SHARED.theme.getPixmap("sticky-on", (fPx, fPx))
|
bulletIcon = SHARED.theme.getToggleIcon("bullet", (fPx, fPx))
|
||||||
stickyOff = SHARED.theme.getPixmap("sticky-off", (fPx, fPx))
|
|
||||||
stickyIcon = QIcon()
|
|
||||||
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
|
|
||||||
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
|
|
||||||
|
|
||||||
bulletOn = SHARED.theme.getPixmap("bullet-on", (fPx, fPx))
|
|
||||||
bulletOff = SHARED.theme.getPixmap("bullet-off", (fPx, fPx))
|
|
||||||
bulletIcon = QIcon()
|
|
||||||
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
|
|
||||||
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
|
|
||||||
|
|
||||||
self.showHide.setIcon(SHARED.theme.getIcon("reference"))
|
self.showHide.setIcon(SHARED.theme.getIcon("reference"))
|
||||||
self.stickyRefs.setIcon(stickyIcon)
|
self.stickyRefs.setIcon(stickyIcon)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from pathlib import Path
|
|||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from urllib.request import pathname2url
|
from urllib.request import pathname2url
|
||||||
|
|
||||||
from PyQt5.QtCore import QUrl, pyqtSlot
|
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtSlot
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
from PyQt5.QtWidgets import QMenuBar, QAction
|
from PyQt5.QtWidgets import QMenuBar, QAction
|
||||||
|
|
||||||
@@ -50,6 +50,11 @@ class GuiMainMenu(QMenuBar):
|
|||||||
add them from this class.
|
add them from this class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
requestDocAction = pyqtSignal(nwDocAction)
|
||||||
|
requestDocInsert = pyqtSignal(nwDocInsert)
|
||||||
|
requestDocInsertText = pyqtSignal(str)
|
||||||
|
requestDocKeyWordInsert = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, mainGui: GuiMain) -> None:
|
def __init__(self, mainGui: GuiMain) -> None:
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
@@ -68,11 +73,6 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self._buildToolsMenu()
|
self._buildToolsMenu()
|
||||||
self._buildHelpMenu()
|
self._buildHelpMenu()
|
||||||
|
|
||||||
# Function Pointers
|
|
||||||
self._docAction = self.mainGui.passDocumentAction
|
|
||||||
self._docInsert = self.mainGui.docEditor.insertText
|
|
||||||
self._insertKeyWord = self.mainGui.docEditor.insertKeyWord
|
|
||||||
|
|
||||||
logger.debug("Ready: GuiMainMenu")
|
logger.debug("Ready: GuiMainMenu")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -209,7 +209,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Document > Close
|
# Document > Close
|
||||||
self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
|
self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
|
||||||
self.aCloseDoc.setShortcut("Ctrl+W")
|
self.aCloseDoc.setShortcut("Ctrl+W")
|
||||||
self.aCloseDoc.triggered.connect(lambda: self.mainGui.closeDocEditor())
|
self.aCloseDoc.triggered.connect(self.mainGui.closeDocEditor)
|
||||||
|
|
||||||
# Document > Separator
|
# Document > Separator
|
||||||
self.docuMenu.addSeparator()
|
self.docuMenu.addSeparator()
|
||||||
@@ -246,12 +246,12 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Edit > Undo
|
# Edit > Undo
|
||||||
self.aEditUndo = self.editMenu.addAction(self.tr("Undo"))
|
self.aEditUndo = self.editMenu.addAction(self.tr("Undo"))
|
||||||
self.aEditUndo.setShortcut("Ctrl+Z")
|
self.aEditUndo.setShortcut("Ctrl+Z")
|
||||||
self.aEditUndo.triggered.connect(lambda: self._docAction(nwDocAction.UNDO))
|
self.aEditUndo.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.UNDO))
|
||||||
|
|
||||||
# Edit > Redo
|
# Edit > Redo
|
||||||
self.aEditRedo = self.editMenu.addAction(self.tr("Redo"))
|
self.aEditRedo = self.editMenu.addAction(self.tr("Redo"))
|
||||||
self.aEditRedo.setShortcut("Ctrl+Y")
|
self.aEditRedo.setShortcut("Ctrl+Y")
|
||||||
self.aEditRedo.triggered.connect(lambda: self._docAction(nwDocAction.REDO))
|
self.aEditRedo.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.REDO))
|
||||||
|
|
||||||
# Edit > Separator
|
# Edit > Separator
|
||||||
self.editMenu.addSeparator()
|
self.editMenu.addSeparator()
|
||||||
@@ -259,17 +259,17 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Edit > Cut
|
# Edit > Cut
|
||||||
self.aEditCut = self.editMenu.addAction(self.tr("Cut"))
|
self.aEditCut = self.editMenu.addAction(self.tr("Cut"))
|
||||||
self.aEditCut.setShortcut("Ctrl+X")
|
self.aEditCut.setShortcut("Ctrl+X")
|
||||||
self.aEditCut.triggered.connect(lambda: self._docAction(nwDocAction.CUT))
|
self.aEditCut.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.CUT))
|
||||||
|
|
||||||
# Edit > Copy
|
# Edit > Copy
|
||||||
self.aEditCopy = self.editMenu.addAction(self.tr("Copy"))
|
self.aEditCopy = self.editMenu.addAction(self.tr("Copy"))
|
||||||
self.aEditCopy.setShortcut("Ctrl+C")
|
self.aEditCopy.setShortcut("Ctrl+C")
|
||||||
self.aEditCopy.triggered.connect(lambda: self._docAction(nwDocAction.COPY))
|
self.aEditCopy.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.COPY))
|
||||||
|
|
||||||
# Edit > Paste
|
# Edit > Paste
|
||||||
self.aEditPaste = self.editMenu.addAction(self.tr("Paste"))
|
self.aEditPaste = self.editMenu.addAction(self.tr("Paste"))
|
||||||
self.aEditPaste.setShortcut("Ctrl+V")
|
self.aEditPaste.setShortcut("Ctrl+V")
|
||||||
self.aEditPaste.triggered.connect(lambda: self._docAction(nwDocAction.PASTE))
|
self.aEditPaste.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.PASTE))
|
||||||
|
|
||||||
# Edit > Separator
|
# Edit > Separator
|
||||||
self.editMenu.addSeparator()
|
self.editMenu.addSeparator()
|
||||||
@@ -277,12 +277,12 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Edit > Select All
|
# Edit > Select All
|
||||||
self.aSelectAll = self.editMenu.addAction(self.tr("Select All"))
|
self.aSelectAll = self.editMenu.addAction(self.tr("Select All"))
|
||||||
self.aSelectAll.setShortcut("Ctrl+A")
|
self.aSelectAll.setShortcut("Ctrl+A")
|
||||||
self.aSelectAll.triggered.connect(lambda: self._docAction(nwDocAction.SEL_ALL))
|
self.aSelectAll.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.SEL_ALL))
|
||||||
|
|
||||||
# Edit > Select Paragraph
|
# Edit > Select Paragraph
|
||||||
self.aSelectPar = self.editMenu.addAction(self.tr("Select Paragraph"))
|
self.aSelectPar = self.editMenu.addAction(self.tr("Select Paragraph"))
|
||||||
self.aSelectPar.setShortcut("Ctrl+Shift+A")
|
self.aSelectPar.setShortcut("Ctrl+Shift+A")
|
||||||
self.aSelectPar.triggered.connect(lambda: self._docAction(nwDocAction.SEL_PARA))
|
self.aSelectPar.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.SEL_PARA))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -330,7 +330,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# View > Focus Mode
|
# View > Focus Mode
|
||||||
self.aFocusMode = self.viewMenu.addAction(self.tr("Focus Mode"))
|
self.aFocusMode = self.viewMenu.addAction(self.tr("Focus Mode"))
|
||||||
self.aFocusMode.setShortcut("F8")
|
self.aFocusMode.setShortcut("F8")
|
||||||
self.aFocusMode.triggered.connect(lambda: self.mainGui.toggleFocusMode())
|
self.aFocusMode.triggered.connect(self.mainGui.toggleFocusMode)
|
||||||
|
|
||||||
# View > Toggle Full Screen
|
# View > Toggle Full Screen
|
||||||
self.aFullScreen = self.viewMenu.addAction(self.tr("Full Screen Mode"))
|
self.aFullScreen = self.viewMenu.addAction(self.tr("Full Screen Mode"))
|
||||||
@@ -350,22 +350,30 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Insert > Short Dash
|
# Insert > Short Dash
|
||||||
self.aInsENDash = self.mInsDashes.addAction(self.tr("Short Dash"))
|
self.aInsENDash = self.mInsDashes.addAction(self.tr("Short Dash"))
|
||||||
self.aInsENDash.setShortcut("Ctrl+K, -")
|
self.aInsENDash.setShortcut("Ctrl+K, -")
|
||||||
self.aInsENDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_ENDASH))
|
self.aInsENDash.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_ENDASH)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Long Dash
|
# Insert > Long Dash
|
||||||
self.aInsEMDash = self.mInsDashes.addAction(self.tr("Long Dash"))
|
self.aInsEMDash = self.mInsDashes.addAction(self.tr("Long Dash"))
|
||||||
self.aInsEMDash.setShortcut("Ctrl+K, _")
|
self.aInsEMDash.setShortcut("Ctrl+K, _")
|
||||||
self.aInsEMDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_EMDASH))
|
self.aInsEMDash.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_EMDASH)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Long Dash
|
# Insert > Long Dash
|
||||||
self.aInsHorBar = self.mInsDashes.addAction(self.tr("Horizontal Bar"))
|
self.aInsHorBar = self.mInsDashes.addAction(self.tr("Horizontal Bar"))
|
||||||
self.aInsHorBar.setShortcut("Ctrl+K, Ctrl+_")
|
self.aInsHorBar.setShortcut("Ctrl+K, Ctrl+_")
|
||||||
self.aInsHorBar.triggered.connect(lambda: self._docInsert(nwUnicode.U_HBAR))
|
self.aInsHorBar.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_HBAR)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Figure Dash
|
# Insert > Figure Dash
|
||||||
self.aInsFigDash = self.mInsDashes.addAction(self.tr("Figure Dash"))
|
self.aInsFigDash = self.mInsDashes.addAction(self.tr("Figure Dash"))
|
||||||
self.aInsFigDash.setShortcut("Ctrl+K, ~")
|
self.aInsFigDash.setShortcut("Ctrl+K, ~")
|
||||||
self.aInsFigDash.triggered.connect(lambda: self._docInsert(nwUnicode.U_FGDASH))
|
self.aInsFigDash.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_FGDASH)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Quote Marks
|
# Insert > Quote Marks
|
||||||
self.mInsQuotes = self.insMenu.addMenu(self.tr("Quote Marks"))
|
self.mInsQuotes = self.insMenu.addMenu(self.tr("Quote Marks"))
|
||||||
@@ -373,27 +381,37 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Insert > Left Single Quote
|
# Insert > Left Single Quote
|
||||||
self.aInsQuoteLS = self.mInsQuotes.addAction(self.tr("Left Single Quote"))
|
self.aInsQuoteLS = self.mInsQuotes.addAction(self.tr("Left Single Quote"))
|
||||||
self.aInsQuoteLS.setShortcut("Ctrl+K, 1")
|
self.aInsQuoteLS.setShortcut("Ctrl+K, 1")
|
||||||
self.aInsQuoteLS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LS))
|
self.aInsQuoteLS.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_LS)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Right Single Quote
|
# Insert > Right Single Quote
|
||||||
self.aInsQuoteRS = self.mInsQuotes.addAction(self.tr("Right Single Quote"))
|
self.aInsQuoteRS = self.mInsQuotes.addAction(self.tr("Right Single Quote"))
|
||||||
self.aInsQuoteRS.setShortcut("Ctrl+K, 2")
|
self.aInsQuoteRS.setShortcut("Ctrl+K, 2")
|
||||||
self.aInsQuoteRS.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RS))
|
self.aInsQuoteRS.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_RS)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Left Double Quote
|
# Insert > Left Double Quote
|
||||||
self.aInsQuoteLD = self.mInsQuotes.addAction(self.tr("Left Double Quote"))
|
self.aInsQuoteLD = self.mInsQuotes.addAction(self.tr("Left Double Quote"))
|
||||||
self.aInsQuoteLD.setShortcut("Ctrl+K, 3")
|
self.aInsQuoteLD.setShortcut("Ctrl+K, 3")
|
||||||
self.aInsQuoteLD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_LD))
|
self.aInsQuoteLD.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_LD)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Right Double Quote
|
# Insert > Right Double Quote
|
||||||
self.aInsQuoteRD = self.mInsQuotes.addAction(self.tr("Right Double Quote"))
|
self.aInsQuoteRD = self.mInsQuotes.addAction(self.tr("Right Double Quote"))
|
||||||
self.aInsQuoteRD.setShortcut("Ctrl+K, 4")
|
self.aInsQuoteRD.setShortcut("Ctrl+K, 4")
|
||||||
self.aInsQuoteRD.triggered.connect(lambda: self._docInsert(nwDocInsert.QUOTE_RD))
|
self.aInsQuoteRD.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.QUOTE_RD)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Alternative Apostrophe
|
# Insert > Alternative Apostrophe
|
||||||
self.aInsMSApos = self.mInsQuotes.addAction(self.tr("Alternative Apostrophe"))
|
self.aInsMSApos = self.mInsQuotes.addAction(self.tr("Alternative Apostrophe"))
|
||||||
self.aInsMSApos.setShortcut("Ctrl+K, '")
|
self.aInsMSApos.setShortcut("Ctrl+K, '")
|
||||||
self.aInsMSApos.triggered.connect(lambda: self._docInsert(nwUnicode.U_MAPOSS))
|
self.aInsMSApos.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_MAPOSS)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Symbols
|
# Insert > Symbols
|
||||||
self.mInsPunct = self.insMenu.addMenu(self.tr("General Punctuation"))
|
self.mInsPunct = self.insMenu.addMenu(self.tr("General Punctuation"))
|
||||||
@@ -401,17 +419,23 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Insert > Ellipsis
|
# Insert > Ellipsis
|
||||||
self.aInsEllipsis = self.mInsPunct.addAction(self.tr("Ellipsis"))
|
self.aInsEllipsis = self.mInsPunct.addAction(self.tr("Ellipsis"))
|
||||||
self.aInsEllipsis.setShortcut("Ctrl+K, .")
|
self.aInsEllipsis.setShortcut("Ctrl+K, .")
|
||||||
self.aInsEllipsis.triggered.connect(lambda: self._docInsert(nwUnicode.U_HELLIP))
|
self.aInsEllipsis.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_HELLIP)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Prime
|
# Insert > Prime
|
||||||
self.aInsPrime = self.mInsPunct.addAction(self.tr("Prime"))
|
self.aInsPrime = self.mInsPunct.addAction(self.tr("Prime"))
|
||||||
self.aInsPrime.setShortcut("Ctrl+K, Ctrl+'")
|
self.aInsPrime.setShortcut("Ctrl+K, Ctrl+'")
|
||||||
self.aInsPrime.triggered.connect(lambda: self._docInsert(nwUnicode.U_PRIME))
|
self.aInsPrime.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_PRIME)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Double Prime
|
# Insert > Double Prime
|
||||||
self.aInsDPrime = self.mInsPunct.addAction(self.tr("Double Prime"))
|
self.aInsDPrime = self.mInsPunct.addAction(self.tr("Double Prime"))
|
||||||
self.aInsDPrime.setShortcut("Ctrl+K, Ctrl+\"")
|
self.aInsDPrime.setShortcut("Ctrl+K, Ctrl+\"")
|
||||||
self.aInsDPrime.triggered.connect(lambda: self._docInsert(nwUnicode.U_DPRIME))
|
self.aInsDPrime.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_DPRIME)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > White Spaces
|
# Insert > White Spaces
|
||||||
self.mInsSpace = self.insMenu.addMenu(self.tr("White Spaces"))
|
self.mInsSpace = self.insMenu.addMenu(self.tr("White Spaces"))
|
||||||
@@ -419,17 +443,23 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Insert > Non-Breaking Space
|
# Insert > Non-Breaking Space
|
||||||
self.aInsNBSpace = self.mInsSpace.addAction(self.tr("Non-Breaking Space"))
|
self.aInsNBSpace = self.mInsSpace.addAction(self.tr("Non-Breaking Space"))
|
||||||
self.aInsNBSpace.setShortcut("Ctrl+K, Space")
|
self.aInsNBSpace.setShortcut("Ctrl+K, Space")
|
||||||
self.aInsNBSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_NBSP))
|
self.aInsNBSpace.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_NBSP)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Thin Space
|
# Insert > Thin Space
|
||||||
self.aInsThinSpace = self.mInsSpace.addAction(self.tr("Thin Space"))
|
self.aInsThinSpace = self.mInsSpace.addAction(self.tr("Thin Space"))
|
||||||
self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space")
|
self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space")
|
||||||
self.aInsThinSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_THSP))
|
self.aInsThinSpace.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_THSP)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Thin Non-Breaking Space
|
# Insert > Thin Non-Breaking Space
|
||||||
self.aInsThinNBSpace = self.mInsSpace.addAction(self.tr("Thin Non-Breaking Space"))
|
self.aInsThinNBSpace = self.mInsSpace.addAction(self.tr("Thin Non-Breaking Space"))
|
||||||
self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space")
|
self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space")
|
||||||
self.aInsThinNBSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_THNBSP))
|
self.aInsThinNBSpace.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_THNBSP)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Symbols
|
# Insert > Symbols
|
||||||
self.mInsSymbol = self.insMenu.addMenu(self.tr("Other Symbols"))
|
self.mInsSymbol = self.insMenu.addMenu(self.tr("Other Symbols"))
|
||||||
@@ -437,42 +467,58 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Insert > List Bullet
|
# Insert > List Bullet
|
||||||
self.aInsBullet = self.mInsSymbol.addAction(self.tr("List Bullet"))
|
self.aInsBullet = self.mInsSymbol.addAction(self.tr("List Bullet"))
|
||||||
self.aInsBullet.setShortcut("Ctrl+K, *")
|
self.aInsBullet.setShortcut("Ctrl+K, *")
|
||||||
self.aInsBullet.triggered.connect(lambda: self._docInsert(nwUnicode.U_BULL))
|
self.aInsBullet.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_BULL)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Hyphen Bullet
|
# Insert > Hyphen Bullet
|
||||||
self.aInsHyBull = self.mInsSymbol.addAction(self.tr("Hyphen Bullet"))
|
self.aInsHyBull = self.mInsSymbol.addAction(self.tr("Hyphen Bullet"))
|
||||||
self.aInsHyBull.setShortcut("Ctrl+K, Ctrl+-")
|
self.aInsHyBull.setShortcut("Ctrl+K, Ctrl+-")
|
||||||
self.aInsHyBull.triggered.connect(lambda: self._docInsert(nwUnicode.U_HYBULL))
|
self.aInsHyBull.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_HYBULL)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Flower Mark
|
# Insert > Flower Mark
|
||||||
self.aInsFlower = self.mInsSymbol.addAction(self.tr("Flower Mark"))
|
self.aInsFlower = self.mInsSymbol.addAction(self.tr("Flower Mark"))
|
||||||
self.aInsFlower.setShortcut("Ctrl+K, Ctrl+*")
|
self.aInsFlower.setShortcut("Ctrl+K, Ctrl+*")
|
||||||
self.aInsFlower.triggered.connect(lambda: self._docInsert(nwUnicode.U_FLOWER))
|
self.aInsFlower.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_FLOWER)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Per Mille
|
# Insert > Per Mille
|
||||||
self.aInsPerMille = self.mInsSymbol.addAction(self.tr("Per Mille"))
|
self.aInsPerMille = self.mInsSymbol.addAction(self.tr("Per Mille"))
|
||||||
self.aInsPerMille.setShortcut("Ctrl+K, %")
|
self.aInsPerMille.setShortcut("Ctrl+K, %")
|
||||||
self.aInsPerMille.triggered.connect(lambda: self._docInsert(nwUnicode.U_PERMIL))
|
self.aInsPerMille.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_PERMIL)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Degree Symbol
|
# Insert > Degree Symbol
|
||||||
self.aInsDegree = self.mInsSymbol.addAction(self.tr("Degree Symbol"))
|
self.aInsDegree = self.mInsSymbol.addAction(self.tr("Degree Symbol"))
|
||||||
self.aInsDegree.setShortcut("Ctrl+K, Ctrl+O")
|
self.aInsDegree.setShortcut("Ctrl+K, Ctrl+O")
|
||||||
self.aInsDegree.triggered.connect(lambda: self._docInsert(nwUnicode.U_DEGREE))
|
self.aInsDegree.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_DEGREE)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Minus Sign
|
# Insert > Minus Sign
|
||||||
self.aInsMinus = self.mInsSymbol.addAction(self.tr("Minus Sign"))
|
self.aInsMinus = self.mInsSymbol.addAction(self.tr("Minus Sign"))
|
||||||
self.aInsMinus.setShortcut("Ctrl+K, Ctrl+M")
|
self.aInsMinus.setShortcut("Ctrl+K, Ctrl+M")
|
||||||
self.aInsMinus.triggered.connect(lambda: self._docInsert(nwUnicode.U_MINUS))
|
self.aInsMinus.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_MINUS)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Times Sign
|
# Insert > Times Sign
|
||||||
self.aInsTimes = self.mInsSymbol.addAction(self.tr("Times Sign"))
|
self.aInsTimes = self.mInsSymbol.addAction(self.tr("Times Sign"))
|
||||||
self.aInsTimes.setShortcut("Ctrl+K, Ctrl+X")
|
self.aInsTimes.setShortcut("Ctrl+K, Ctrl+X")
|
||||||
self.aInsTimes.triggered.connect(lambda: self._docInsert(nwUnicode.U_TIMES))
|
self.aInsTimes.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_TIMES)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Division
|
# Insert > Division
|
||||||
self.aInsDivide = self.mInsSymbol.addAction(self.tr("Division Sign"))
|
self.aInsDivide = self.mInsSymbol.addAction(self.tr("Division Sign"))
|
||||||
self.aInsDivide.setShortcut("Ctrl+K, Ctrl+D")
|
self.aInsDivide.setShortcut("Ctrl+K, Ctrl+D")
|
||||||
self.aInsDivide.triggered.connect(lambda: self._docInsert(nwUnicode.U_DIVIDE))
|
self.aInsDivide.triggered.connect(
|
||||||
|
lambda: self.requestDocInsertText.emit(nwUnicode.U_DIVIDE)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Tags and References
|
# Insert > Tags and References
|
||||||
self.mInsKeywords = self.insMenu.addMenu(self.tr("Tags and References"))
|
self.mInsKeywords = self.insMenu.addMenu(self.tr("Tags and References"))
|
||||||
@@ -491,7 +537,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.mInsKWItems[keyWord][0].setText(trConst(nwLabels.KEY_NAME[keyWord]))
|
self.mInsKWItems[keyWord][0].setText(trConst(nwLabels.KEY_NAME[keyWord]))
|
||||||
self.mInsKWItems[keyWord][0].setShortcut(self.mInsKWItems[keyWord][1])
|
self.mInsKWItems[keyWord][0].setShortcut(self.mInsKWItems[keyWord][1])
|
||||||
self.mInsKWItems[keyWord][0].triggered.connect(
|
self.mInsKWItems[keyWord][0].triggered.connect(
|
||||||
lambda n, keyWord=keyWord: self._insertKeyWord(keyWord)
|
lambda n, keyWord=keyWord: self.requestDocKeyWordInsert.emit(keyWord)
|
||||||
)
|
)
|
||||||
self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0])
|
self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0])
|
||||||
|
|
||||||
@@ -501,22 +547,30 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Insert > Synopsis Comment
|
# Insert > Synopsis Comment
|
||||||
self.aInsSynopsis = self.mInsComments.addAction(self.tr("Synopsis Comment"))
|
self.aInsSynopsis = self.mInsComments.addAction(self.tr("Synopsis Comment"))
|
||||||
self.aInsSynopsis.setShortcut("Ctrl+K, S")
|
self.aInsSynopsis.setShortcut("Ctrl+K, S")
|
||||||
self.aInsSynopsis.triggered.connect(lambda: self._docInsert(nwDocInsert.SYNOPSIS))
|
self.aInsSynopsis.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.SYNOPSIS)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Symbols
|
# Insert > Symbols
|
||||||
self.mInsBreaks = self.insMenu.addMenu(self.tr("Page Break and Space"))
|
self.mInsBreaks = self.insMenu.addMenu(self.tr("Page Break and Space"))
|
||||||
|
|
||||||
# Insert > New Page
|
# Insert > New Page
|
||||||
self.aInsNewPage = self.mInsBreaks.addAction(self.tr("Page Break"))
|
self.aInsNewPage = self.mInsBreaks.addAction(self.tr("Page Break"))
|
||||||
self.aInsNewPage.triggered.connect(lambda: self._docInsert(nwDocInsert.NEW_PAGE))
|
self.aInsNewPage.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.NEW_PAGE)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Vertical Space (Single)
|
# Insert > Vertical Space (Single)
|
||||||
self.aInsVSpaceS = self.mInsBreaks.addAction(self.tr("Vertical Space (Single)"))
|
self.aInsVSpaceS = self.mInsBreaks.addAction(self.tr("Vertical Space (Single)"))
|
||||||
self.aInsVSpaceS.triggered.connect(lambda: self._docInsert(nwDocInsert.VSPACE_S))
|
self.aInsVSpaceS.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.VSPACE_S)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Vertical Space (Multi)
|
# Insert > Vertical Space (Multi)
|
||||||
self.aInsVSpaceM = self.mInsBreaks.addAction(self.tr("Vertical Space (Multi)"))
|
self.aInsVSpaceM = self.mInsBreaks.addAction(self.tr("Vertical Space (Multi)"))
|
||||||
self.aInsVSpaceM.triggered.connect(lambda: self._docInsert(nwDocInsert.VSPACE_M))
|
self.aInsVSpaceM.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.VSPACE_M)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Placeholder Text
|
# Insert > Placeholder Text
|
||||||
self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text"))
|
self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text"))
|
||||||
@@ -532,17 +586,23 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Format > Emphasis
|
# Format > Emphasis
|
||||||
self.aFmtEmph = self.fmtMenu.addAction(self.tr("Emphasis"))
|
self.aFmtEmph = self.fmtMenu.addAction(self.tr("Emphasis"))
|
||||||
self.aFmtEmph.setShortcut("Ctrl+I")
|
self.aFmtEmph.setShortcut("Ctrl+I")
|
||||||
self.aFmtEmph.triggered.connect(lambda: self._docAction(nwDocAction.EMPH))
|
self.aFmtEmph.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.EMPH)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Strong Emphasis
|
# Format > Strong Emphasis
|
||||||
self.aFmtStrong = self.fmtMenu.addAction(self.tr("Strong Emphasis"))
|
self.aFmtStrong = self.fmtMenu.addAction(self.tr("Strong Emphasis"))
|
||||||
self.aFmtStrong.setShortcut("Ctrl+B")
|
self.aFmtStrong.setShortcut("Ctrl+B")
|
||||||
self.aFmtStrong.triggered.connect(lambda: self._docAction(nwDocAction.STRONG))
|
self.aFmtStrong.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.STRONG)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Strikethrough
|
# Format > Strikethrough
|
||||||
self.aFmtStrike = self.fmtMenu.addAction(self.tr("Strikethrough"))
|
self.aFmtStrike = self.fmtMenu.addAction(self.tr("Strikethrough"))
|
||||||
self.aFmtStrike.setShortcut("Ctrl+D")
|
self.aFmtStrike.setShortcut("Ctrl+D")
|
||||||
self.aFmtStrike.triggered.connect(lambda: self._docAction(nwDocAction.STRIKE))
|
self.aFmtStrike.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.STRIKE)
|
||||||
|
)
|
||||||
|
|
||||||
# Edit > Separator
|
# Edit > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
@@ -550,12 +610,16 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Format > Double Quotes
|
# Format > Double Quotes
|
||||||
self.aFmtDQuote = self.fmtMenu.addAction(self.tr("Wrap Double Quotes"))
|
self.aFmtDQuote = self.fmtMenu.addAction(self.tr("Wrap Double Quotes"))
|
||||||
self.aFmtDQuote.setShortcut("Ctrl+\"")
|
self.aFmtDQuote.setShortcut("Ctrl+\"")
|
||||||
self.aFmtDQuote.triggered.connect(lambda: self._docAction(nwDocAction.D_QUOTE))
|
self.aFmtDQuote.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.D_QUOTE)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Single Quotes
|
# Format > Single Quotes
|
||||||
self.aFmtSQuote = self.fmtMenu.addAction(self.tr("Wrap Single Quotes"))
|
self.aFmtSQuote = self.fmtMenu.addAction(self.tr("Wrap Single Quotes"))
|
||||||
self.aFmtSQuote.setShortcut("Ctrl+'")
|
self.aFmtSQuote.setShortcut("Ctrl+'")
|
||||||
self.aFmtSQuote.triggered.connect(lambda: self._docAction(nwDocAction.S_QUOTE))
|
self.aFmtSQuote.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.S_QUOTE)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
@@ -564,28 +628,40 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.mShortcodes = self.fmtMenu.addMenu(self.tr("More Formats ..."))
|
self.mShortcodes = self.fmtMenu.addMenu(self.tr("More Formats ..."))
|
||||||
|
|
||||||
# Shortcode Italic
|
# Shortcode Italic
|
||||||
self.aScItalic = self.mShortcodes.addAction(self.tr("Italics Shortcode"))
|
self.aScItalic = self.mShortcodes.addAction(self.tr("Italics (Shortcode)"))
|
||||||
self.aScItalic.triggered.connect(lambda: self._docAction(nwDocAction.SC_ITALIC))
|
self.aScItalic.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_ITALIC)
|
||||||
|
)
|
||||||
|
|
||||||
# Shortcode Bold
|
# Shortcode Bold
|
||||||
self.aScBold = self.mShortcodes.addAction(self.tr("Bold Shortcode"))
|
self.aScBold = self.mShortcodes.addAction(self.tr("Bold (Shortcode)"))
|
||||||
self.aScBold.triggered.connect(lambda: self._docAction(nwDocAction.SC_BOLD))
|
self.aScBold.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_BOLD)
|
||||||
# Shortcode Underline
|
)
|
||||||
self.aScULine = self.mShortcodes.addAction(self.tr("Underline Shortcode"))
|
|
||||||
self.aScULine.triggered.connect(lambda: self._docAction(nwDocAction.SC_ULINE))
|
|
||||||
|
|
||||||
# Shortcode Strikethrough
|
# Shortcode Strikethrough
|
||||||
self.aScStrike = self.mShortcodes.addAction(self.tr("Strikethrough Shortcode"))
|
self.aScStrike = self.mShortcodes.addAction(self.tr("Strikethrough (Shortcode)"))
|
||||||
self.aScStrike.triggered.connect(lambda: self._docAction(nwDocAction.SC_STRIKE))
|
self.aScStrike.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_STRIKE)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Shortcode Underline
|
||||||
|
self.aScULine = self.mShortcodes.addAction(self.tr("Underline"))
|
||||||
|
self.aScULine.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_ULINE)
|
||||||
|
)
|
||||||
|
|
||||||
# Shortcode Superscript
|
# Shortcode Superscript
|
||||||
self.aScSuper = self.mShortcodes.addAction(self.tr("Superscript"))
|
self.aScSuper = self.mShortcodes.addAction(self.tr("Superscript"))
|
||||||
self.aScSuper.triggered.connect(lambda: self._docAction(nwDocAction.SC_SUP))
|
self.aScSuper.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_SUP)
|
||||||
|
)
|
||||||
|
|
||||||
# Shortcode Subscript
|
# Shortcode Subscript
|
||||||
self.aScSub = self.mShortcodes.addAction(self.tr("Subscript"))
|
self.aScSub = self.mShortcodes.addAction(self.tr("Subscript"))
|
||||||
self.aScSub.triggered.connect(lambda: self._docAction(nwDocAction.SC_SUB))
|
self.aScSub.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.SC_SUB)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
@@ -593,33 +669,45 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Format > Header 1 (Partition)
|
# Format > Header 1 (Partition)
|
||||||
self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Header 1 (Partition)"))
|
self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Header 1 (Partition)"))
|
||||||
self.aFmtHead1.setShortcut("Ctrl+1")
|
self.aFmtHead1.setShortcut("Ctrl+1")
|
||||||
self.aFmtHead1.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H1))
|
self.aFmtHead1.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H1)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Header 2 (Chapter)
|
# Format > Header 2 (Chapter)
|
||||||
self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Header 2 (Chapter)"))
|
self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Header 2 (Chapter)"))
|
||||||
self.aFmtHead2.setShortcut("Ctrl+2")
|
self.aFmtHead2.setShortcut("Ctrl+2")
|
||||||
self.aFmtHead2.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H2))
|
self.aFmtHead2.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H2)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Header 3 (Scene)
|
# Format > Header 3 (Scene)
|
||||||
self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Header 3 (Scene)"))
|
self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Header 3 (Scene)"))
|
||||||
self.aFmtHead3.setShortcut("Ctrl+3")
|
self.aFmtHead3.setShortcut("Ctrl+3")
|
||||||
self.aFmtHead3.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H3))
|
self.aFmtHead3.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H3)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Header 4 (Section)
|
# Format > Header 4 (Section)
|
||||||
self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Header 4 (Section)"))
|
self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Header 4 (Section)"))
|
||||||
self.aFmtHead4.setShortcut("Ctrl+4")
|
self.aFmtHead4.setShortcut("Ctrl+4")
|
||||||
self.aFmtHead4.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_H4))
|
self.aFmtHead4.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H4)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
|
|
||||||
# Format > Novel Title
|
# Format > Novel Title
|
||||||
self.aFmtTitle = self.fmtMenu.addAction(self.tr("Novel Title"))
|
self.aFmtTitle = self.fmtMenu.addAction(self.tr("Novel Title"))
|
||||||
self.aFmtTitle.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_TTL))
|
self.aFmtTitle.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_TTL)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Unnumbered Chapter
|
# Format > Unnumbered Chapter
|
||||||
self.aFmtUnNum = self.fmtMenu.addAction(self.tr("Unnumbered Chapter"))
|
self.aFmtUnNum = self.fmtMenu.addAction(self.tr("Unnumbered Chapter"))
|
||||||
self.aFmtUnNum.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_UNN))
|
self.aFmtUnNum.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
@@ -627,17 +715,23 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Format > Align Left
|
# Format > Align Left
|
||||||
self.aFmtAlignLeft = self.fmtMenu.addAction(self.tr("Align Left"))
|
self.aFmtAlignLeft = self.fmtMenu.addAction(self.tr("Align Left"))
|
||||||
self.aFmtAlignLeft.setShortcut("Ctrl+5")
|
self.aFmtAlignLeft.setShortcut("Ctrl+5")
|
||||||
self.aFmtAlignLeft.triggered.connect(lambda: self._docAction(nwDocAction.ALIGN_L))
|
self.aFmtAlignLeft.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.ALIGN_L)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Align Centre
|
# Format > Align Centre
|
||||||
self.aFmtAlignCentre = self.fmtMenu.addAction(self.tr("Align Centre"))
|
self.aFmtAlignCentre = self.fmtMenu.addAction(self.tr("Align Centre"))
|
||||||
self.aFmtAlignCentre.setShortcut("Ctrl+6")
|
self.aFmtAlignCentre.setShortcut("Ctrl+6")
|
||||||
self.aFmtAlignCentre.triggered.connect(lambda: self._docAction(nwDocAction.ALIGN_C))
|
self.aFmtAlignCentre.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.ALIGN_C)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Align Right
|
# Format > Align Right
|
||||||
self.aFmtAlignRight = self.fmtMenu.addAction(self.tr("Align Right"))
|
self.aFmtAlignRight = self.fmtMenu.addAction(self.tr("Align Right"))
|
||||||
self.aFmtAlignRight.setShortcut("Ctrl+7")
|
self.aFmtAlignRight.setShortcut("Ctrl+7")
|
||||||
self.aFmtAlignRight.triggered.connect(lambda: self._docAction(nwDocAction.ALIGN_R))
|
self.aFmtAlignRight.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.ALIGN_R)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
@@ -645,12 +739,16 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Format > Indent Left
|
# Format > Indent Left
|
||||||
self.aFmtIndentLeft = self.fmtMenu.addAction(self.tr("Indent Left"))
|
self.aFmtIndentLeft = self.fmtMenu.addAction(self.tr("Indent Left"))
|
||||||
self.aFmtIndentLeft.setShortcut("Ctrl+8")
|
self.aFmtIndentLeft.setShortcut("Ctrl+8")
|
||||||
self.aFmtIndentLeft.triggered.connect(lambda: self._docAction(nwDocAction.INDENT_L))
|
self.aFmtIndentLeft.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.INDENT_L)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Indent Right
|
# Format > Indent Right
|
||||||
self.aFmtIndentRight = self.fmtMenu.addAction(self.tr("Indent Right"))
|
self.aFmtIndentRight = self.fmtMenu.addAction(self.tr("Indent Right"))
|
||||||
self.aFmtIndentRight.setShortcut("Ctrl+9")
|
self.aFmtIndentRight.setShortcut("Ctrl+9")
|
||||||
self.aFmtIndentRight.triggered.connect(lambda: self._docAction(nwDocAction.INDENT_R))
|
self.aFmtIndentRight.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.INDENT_R)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
@@ -658,27 +756,37 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Format > Comment
|
# Format > Comment
|
||||||
self.aFmtComment = self.fmtMenu.addAction(self.tr("Toggle Comment"))
|
self.aFmtComment = self.fmtMenu.addAction(self.tr("Toggle Comment"))
|
||||||
self.aFmtComment.setShortcut("Ctrl+/")
|
self.aFmtComment.setShortcut("Ctrl+/")
|
||||||
self.aFmtComment.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_COM))
|
self.aFmtComment.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_COM)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Remove Block Format
|
# Format > Remove Block Format
|
||||||
self.aFmtNoFormat = self.fmtMenu.addAction(self.tr("Remove Block Format"))
|
self.aFmtNoFormat = self.fmtMenu.addAction(self.tr("Remove Block Format"))
|
||||||
self.aFmtNoFormat.setShortcuts(["Ctrl+0", "Ctrl+Shift+/"])
|
self.aFmtNoFormat.setShortcuts(["Ctrl+0", "Ctrl+Shift+/"])
|
||||||
self.aFmtNoFormat.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_TXT))
|
self.aFmtNoFormat.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_TXT)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Separator
|
# Format > Separator
|
||||||
self.fmtMenu.addSeparator()
|
self.fmtMenu.addSeparator()
|
||||||
|
|
||||||
# Format > Replace Single Quotes
|
# Format > Replace Single Quotes
|
||||||
self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Convert Single Quotes"))
|
self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Convert Single Quotes"))
|
||||||
self.aFmtReplSng.triggered.connect(lambda: self._docAction(nwDocAction.REPL_SNG))
|
self.aFmtReplSng.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.REPL_SNG)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Replace Double Quotes
|
# Format > Replace Double Quotes
|
||||||
self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Convert Double Quotes"))
|
self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Convert Double Quotes"))
|
||||||
self.aFmtReplDbl.triggered.connect(lambda: self._docAction(nwDocAction.REPL_DBL))
|
self.aFmtReplDbl.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.REPL_DBL)
|
||||||
|
)
|
||||||
|
|
||||||
# Format > Remove In-Paragraph Breaks
|
# Format > Remove In-Paragraph Breaks
|
||||||
self.aFmtRmBreaks = self.fmtMenu.addAction(self.tr("Remove In-Paragraph Breaks"))
|
self.aFmtRmBreaks = self.fmtMenu.addAction(self.tr("Remove In-Paragraph Breaks"))
|
||||||
self.aFmtRmBreaks.triggered.connect(lambda: self._docAction(nwDocAction.RM_BREAKS))
|
self.aFmtRmBreaks.triggered.connect(
|
||||||
|
lambda: self.requestDocAction.emit(nwDocAction.RM_BREAKS)
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ import logging
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtGui import QFont, QPalette
|
from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
|
||||||
from PyQt5.QtCore import Qt, QSize, pyqtSlot, pyqtSignal
|
from PyQt5.QtCore import QModelIndex, QPoint, Qt, QSize, pyqtSlot, pyqtSignal
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
|
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
|
||||||
QInputDialog, QMenu, QSizePolicy, QToolButton, QToolTip, QTreeWidget,
|
QInputDialog, QMenu, QSizePolicy, QToolButton, QToolTip, QTreeWidget,
|
||||||
@@ -42,8 +43,12 @@ from novelwriter import CONFIG, SHARED
|
|||||||
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
||||||
from novelwriter.common import minmax
|
from novelwriter.common import minmax
|
||||||
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
|
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
|
||||||
|
from novelwriter.core.index import IndexHeading
|
||||||
from novelwriter.extensions.novelselector import NovelSelector
|
from novelwriter.extensions.novelselector import NovelSelector
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +68,7 @@ class GuiNovelView(QWidget):
|
|||||||
selectedItemChanged = pyqtSignal(str)
|
selectedItemChanged = pyqtSignal(str)
|
||||||
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
|
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui: GuiMain) -> None:
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -92,33 +97,29 @@ class GuiNovelView(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateTheme(self):
|
def updateTheme(self) -> None:
|
||||||
"""Update theme elements.
|
"""Update theme elements."""
|
||||||
"""
|
|
||||||
self.novelBar.updateTheme()
|
self.novelBar.updateTheme()
|
||||||
self.novelTree.updateTheme()
|
self.novelTree.updateTheme()
|
||||||
self.refreshTree()
|
self.refreshTree()
|
||||||
return
|
return
|
||||||
|
|
||||||
def initSettings(self):
|
def initSettings(self) -> None:
|
||||||
"""Initialise GUI elements that depend on specific settings.
|
"""Initialise GUI elements that depend on specific settings."""
|
||||||
"""
|
|
||||||
self.novelTree.initSettings()
|
self.novelTree.initSettings()
|
||||||
return
|
return
|
||||||
|
|
||||||
def clearNovelView(self):
|
def clearNovelView(self) -> None:
|
||||||
"""Clear project-related GUI content.
|
"""Clear project-related GUI content."""
|
||||||
"""
|
|
||||||
self.novelTree.clearContent()
|
self.novelTree.clearContent()
|
||||||
self.novelBar.clearContent()
|
self.novelBar.clearContent()
|
||||||
self.novelBar.setEnabled(False)
|
self.novelBar.setEnabled(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
def openProjectTasks(self):
|
def openProjectTasks(self) -> None:
|
||||||
"""Run open project tasks.
|
"""Run open project tasks."""
|
||||||
"""
|
|
||||||
lastNovel = SHARED.project.data.getLastHandle("novelTree")
|
lastNovel = SHARED.project.data.getLastHandle("novelTree")
|
||||||
if lastNovel not in SHARED.project.tree:
|
if lastNovel and lastNovel not in SHARED.project.tree:
|
||||||
lastNovel = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
lastNovel = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
||||||
|
|
||||||
logger.debug("Setting novel tree to root item '%s'", lastNovel)
|
logger.debug("Setting novel tree to root item '%s'", lastNovel)
|
||||||
@@ -140,7 +141,7 @@ class GuiNovelView(QWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def closeProjectTasks(self):
|
def closeProjectTasks(self) -> None:
|
||||||
"""Run closing project tasks."""
|
"""Run closing project tasks."""
|
||||||
lastColType = self.novelTree.lastColType
|
lastColType = self.novelTree.lastColType
|
||||||
lastColSize = self.novelTree.lastColSize
|
lastColSize = self.novelTree.lastColSize
|
||||||
@@ -150,15 +151,13 @@ class GuiNovelView(QWidget):
|
|||||||
self.clearNovelView()
|
self.clearNovelView()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTreeFocus(self):
|
def setTreeFocus(self) -> None:
|
||||||
"""Set the focus to the tree widget.
|
"""Set the focus to the tree widget."""
|
||||||
"""
|
|
||||||
self.novelTree.setFocus()
|
self.novelTree.setFocus()
|
||||||
return
|
return
|
||||||
|
|
||||||
def treeHasFocus(self):
|
def treeHasFocus(self) -> bool:
|
||||||
"""Check if the novel tree has focus.
|
"""Check if the novel tree has focus."""
|
||||||
"""
|
|
||||||
return self.novelTree.hasFocus()
|
return self.novelTree.hasFocus()
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -166,21 +165,19 @@ class GuiNovelView(QWidget):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def refreshTree(self):
|
def refreshTree(self) -> None:
|
||||||
"""Refresh the current tree.
|
"""Refresh the current tree."""
|
||||||
"""
|
|
||||||
self.novelTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("novelTree"))
|
self.novelTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("novelTree"))
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def updateRootItem(self, tHandle):
|
def updateRootItem(self, tHandle: str) -> None:
|
||||||
"""If any root item changes, rebuild the novel root menu.
|
"""If any root item changes, rebuild the novel root menu."""
|
||||||
"""
|
|
||||||
self.novelBar.buildNovelRootMenu()
|
self.novelBar.buildNovelRootMenu()
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def updateNovelItemMeta(self, tHandle):
|
def updateNovelItemMeta(self, tHandle: str) -> None:
|
||||||
"""The meta data of a novel item has changed, and the tree item
|
"""The meta data of a novel item has changed, and the tree item
|
||||||
needs to be refreshed.
|
needs to be refreshed.
|
||||||
"""
|
"""
|
||||||
@@ -192,7 +189,7 @@ class GuiNovelView(QWidget):
|
|||||||
|
|
||||||
class GuiNovelToolBar(QWidget):
|
class GuiNovelToolBar(QWidget):
|
||||||
|
|
||||||
def __init__(self, novelView):
|
def __init__(self, novelView: GuiNovelView) -> None:
|
||||||
super().__init__(parent=novelView)
|
super().__init__(parent=novelView)
|
||||||
|
|
||||||
logger.debug("Create: GuiNovelToolBar")
|
logger.debug("Create: GuiNovelToolBar")
|
||||||
@@ -269,9 +266,8 @@ class GuiNovelToolBar(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateTheme(self):
|
def updateTheme(self) -> None:
|
||||||
"""Update theme elements.
|
"""Update theme elements."""
|
||||||
"""
|
|
||||||
# Icons
|
# Icons
|
||||||
self.tbNovel.setIcon(SHARED.theme.getIcon("cls_novel"))
|
self.tbNovel.setIcon(SHARED.theme.getIcon("cls_novel"))
|
||||||
self.tbRefresh.setIcon(SHARED.theme.getIcon("refresh"))
|
self.tbRefresh.setIcon(SHARED.theme.getIcon("refresh"))
|
||||||
@@ -287,10 +283,11 @@ class GuiNovelToolBar(QWidget):
|
|||||||
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
||||||
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
||||||
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||||
|
buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}"
|
||||||
|
|
||||||
self.tbNovel.setStyleSheet(buttonStyle)
|
self.tbNovel.setStyleSheet(buttonStyle)
|
||||||
self.tbRefresh.setStyleSheet(buttonStyle)
|
self.tbRefresh.setStyleSheet(buttonStyle)
|
||||||
self.tbMore.setStyleSheet(buttonStyle)
|
self.tbMore.setStyleSheet(buttonStyleMenu)
|
||||||
|
|
||||||
self.novelValue.setStyleSheet(
|
self.novelValue.setStyleSheet(
|
||||||
"QComboBox {border-style: none; padding-left: 0;} "
|
"QComboBox {border-style: none; padding-left: 0;} "
|
||||||
@@ -301,30 +298,26 @@ class GuiNovelToolBar(QWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def clearContent(self):
|
def clearContent(self) -> None:
|
||||||
"""Run clearing project tasks.
|
"""Run clearing project tasks."""
|
||||||
"""
|
|
||||||
self.novelValue.clear()
|
self.novelValue.clear()
|
||||||
self.novelValue.setToolTip("")
|
self.novelValue.setToolTip("")
|
||||||
return
|
return
|
||||||
|
|
||||||
def buildNovelRootMenu(self):
|
def buildNovelRootMenu(self) -> None:
|
||||||
"""Build the novel root menu.
|
"""Build the novel root menu."""
|
||||||
"""
|
|
||||||
self.novelValue.updateList(prefix=self.novelPrefix)
|
self.novelValue.updateList(prefix=self.novelPrefix)
|
||||||
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCurrentRoot(self, rootHandle):
|
def setCurrentRoot(self, rootHandle: str | None) -> None:
|
||||||
"""Set the current active root handle.
|
"""Set the current active root handle."""
|
||||||
"""
|
|
||||||
self.novelValue.setHandle(rootHandle)
|
self.novelValue.setHandle(rootHandle)
|
||||||
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastColType(self, colType, doRefresh=True):
|
def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None:
|
||||||
"""Set the last column type.
|
"""Set the last column type."""
|
||||||
"""
|
|
||||||
self.aLastCol[colType].setChecked(True)
|
self.aLastCol[colType].setChecked(True)
|
||||||
self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh)
|
self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh)
|
||||||
return
|
return
|
||||||
@@ -334,24 +327,21 @@ class GuiNovelToolBar(QWidget):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _openNovelSelector(self):
|
def _openNovelSelector(self) -> None:
|
||||||
"""Trigger the dropdown list of the novel selector.
|
"""Trigger the dropdown list of the novel selector."""
|
||||||
"""
|
|
||||||
self.novelValue.showPopup()
|
self.novelValue.showPopup()
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _refreshNovelTree(self):
|
def _refreshNovelTree(self) -> None:
|
||||||
"""Rebuild the current tree.
|
"""Rebuild the current tree."""
|
||||||
"""
|
|
||||||
rootHandle = SHARED.project.data.getLastHandle("novelTree")
|
rootHandle = SHARED.project.data.getLastHandle("novelTree")
|
||||||
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _selectLastColumnSize(self):
|
def _selectLastColumnSize(self) -> None:
|
||||||
"""Set the maximum width for the last column.
|
"""Set the maximum width for the last column."""
|
||||||
"""
|
|
||||||
oldSize = self.novelView.novelTree.lastColSize
|
oldSize = self.novelView.novelTree.lastColSize
|
||||||
newSize, isOk = QInputDialog.getInt(
|
newSize, isOk = QInputDialog.getInt(
|
||||||
self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5
|
self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5
|
||||||
@@ -365,9 +355,8 @@ class GuiNovelToolBar(QWidget):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _addLastColAction(self, colType, actionLabel):
|
def _addLastColAction(self, colType, actionLabel) -> None:
|
||||||
"""Add a column selection entry to the last column menu.
|
"""Add a column selection entry to the last column menu."""
|
||||||
"""
|
|
||||||
aLast = self.mLastCol.addAction(actionLabel)
|
aLast = self.mLastCol.addAction(actionLabel)
|
||||||
aLast.setCheckable(True)
|
aLast.setCheckable(True)
|
||||||
aLast.setActionGroup(self.gLastCol)
|
aLast.setActionGroup(self.gLastCol)
|
||||||
@@ -391,7 +380,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
D_KEY = Qt.ItemDataRole.UserRole + 2
|
D_KEY = Qt.ItemDataRole.UserRole + 2
|
||||||
D_EXTRA = Qt.ItemDataRole.UserRole + 3
|
D_EXTRA = Qt.ItemDataRole.UserRole + 3
|
||||||
|
|
||||||
def __init__(self, novelView):
|
def __init__(self, novelView: GuiNovelView) -> None:
|
||||||
super().__init__(parent=novelView)
|
super().__init__(parent=novelView)
|
||||||
|
|
||||||
logger.debug("Create: GuiNovelTree")
|
logger.debug("Create: GuiNovelTree")
|
||||||
@@ -461,9 +450,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def initSettings(self):
|
def initSettings(self) -> None:
|
||||||
"""Set or update tree widget settings.
|
"""Set or update tree widget settings."""
|
||||||
"""
|
|
||||||
# Scroll bars
|
# Scroll bars
|
||||||
if CONFIG.hideVScroll:
|
if CONFIG.hideVScroll:
|
||||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
@@ -477,11 +465,10 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def updateTheme(self):
|
def updateTheme(self) -> None:
|
||||||
"""Update theme elements.
|
"""Update theme elements."""
|
||||||
"""
|
|
||||||
iPx = SHARED.theme.baseIconSize
|
iPx = SHARED.theme.baseIconSize
|
||||||
self._pMore = SHARED.theme.loadDecoration("deco_doc_more", pxH=iPx)
|
self._pMore = SHARED.theme.loadDecoration("deco_doc_more", h=iPx)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -489,28 +476,26 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lastColType(self):
|
def lastColType(self) -> NovelTreeColumn:
|
||||||
return self._lastCol
|
return self._lastCol
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lastColSize(self):
|
def lastColSize(self) -> int:
|
||||||
return int(self._lastColSize * 100)
|
return int(self._lastColSize * 100)
|
||||||
|
|
||||||
##
|
##
|
||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def clearContent(self):
|
def clearContent(self) -> None:
|
||||||
"""Clear the GUI content and the related maps.
|
"""Clear the GUI content and the related maps."""
|
||||||
"""
|
|
||||||
self.clear()
|
self.clear()
|
||||||
self._treeMap = {}
|
self._treeMap = {}
|
||||||
self._lastBuild = 0
|
self._lastBuild = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def refreshTree(self, rootHandle=None, overRide=False):
|
def refreshTree(self, rootHandle: str | None = None, overRide: bool = False) -> None:
|
||||||
"""Refresh the tree if it has been changed.
|
"""Refresh the tree if it has been changed."""
|
||||||
"""
|
|
||||||
logger.debug("Requesting refresh of the novel tree")
|
logger.debug("Requesting refresh of the novel tree")
|
||||||
if rootHandle is None:
|
if rootHandle is None:
|
||||||
rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
|
||||||
@@ -534,9 +519,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def refreshHandle(self, tHandle):
|
def refreshHandle(self, tHandle: str) -> None:
|
||||||
"""Refresh the data for a given handle.
|
"""Refresh the data for a given handle."""
|
||||||
"""
|
|
||||||
idxData = SHARED.project.index.getItemData(tHandle)
|
idxData = SHARED.project.index.getItemData(tHandle)
|
||||||
if idxData is None:
|
if idxData is None:
|
||||||
return
|
return
|
||||||
@@ -554,7 +538,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def getSelectedHandle(self):
|
def getSelectedHandle(self) -> tuple[str | None, str | None]:
|
||||||
"""Get the currently selected or active handle. If multiple
|
"""Get the currently selected or active handle. If multiple
|
||||||
items are selected, return the first.
|
items are selected, return the first.
|
||||||
"""
|
"""
|
||||||
@@ -566,9 +550,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
return tHandle, sTitle
|
return tHandle, sTitle
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
def setLastColType(self, colType, doRefresh=True):
|
def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None:
|
||||||
"""Change the content type of the last column and rebuild.
|
"""Change the content type of the last column and rebuild."""
|
||||||
"""
|
|
||||||
if self._lastCol != colType:
|
if self._lastCol != colType:
|
||||||
logger.debug("Changing last column to %s", colType.name)
|
logger.debug("Changing last column to %s", colType.name)
|
||||||
self._lastCol = colType
|
self._lastCol = colType
|
||||||
@@ -578,15 +561,13 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
self.refreshTree(rootHandle=lastNovel, overRide=True)
|
self.refreshTree(rootHandle=lastNovel, overRide=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastColSize(self, colSize):
|
def setLastColSize(self, colSize: int) -> None:
|
||||||
"""Set the column size in integer values between 15 and 75.
|
"""Set the column size in integer values between 15 and 75."""
|
||||||
"""
|
|
||||||
self._lastColSize = minmax(colSize, 15, 75)/100.0
|
self._lastColSize = minmax(colSize, 15, 75)/100.0
|
||||||
return
|
return
|
||||||
|
|
||||||
def setActiveHandle(self, tHandle):
|
def setActiveHandle(self, tHandle: str | None) -> None:
|
||||||
"""Highlight the rows associated with a given handle.
|
"""Highlight the rows associated with a given handle."""
|
||||||
"""
|
|
||||||
tStart = time()
|
tStart = time()
|
||||||
|
|
||||||
self._actHandle = tHandle
|
self._actHandle = tHandle
|
||||||
@@ -612,20 +593,20 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
# Events
|
# Events
|
||||||
##
|
##
|
||||||
|
|
||||||
def mousePressEvent(self, theEvent):
|
def mousePressEvent(self, event: QMouseEvent) -> None:
|
||||||
"""Overload mousePressEvent to clear selection if clicking the
|
"""Overload mousePressEvent to clear selection if clicking the
|
||||||
mouse in a blank area of the tree view, and to load a document
|
mouse in a blank area of the tree view, and to load a document
|
||||||
for viewing if the user middle-clicked.
|
for viewing if the user middle-clicked.
|
||||||
"""
|
"""
|
||||||
super().mousePressEvent(theEvent)
|
super().mousePressEvent(event)
|
||||||
|
|
||||||
if theEvent.button() == Qt.LeftButton:
|
if event.button() == Qt.LeftButton:
|
||||||
selItem = self.indexAt(theEvent.pos())
|
selItem = self.indexAt(event.pos())
|
||||||
if not selItem.isValid():
|
if not selItem.isValid():
|
||||||
self.clearSelection()
|
self.clearSelection()
|
||||||
|
|
||||||
elif theEvent.button() == Qt.MiddleButton:
|
elif event.button() == Qt.MiddleButton:
|
||||||
selItem = self.itemAt(theEvent.pos())
|
selItem = self.itemAt(event.pos())
|
||||||
if not isinstance(selItem, QTreeWidgetItem):
|
if not isinstance(selItem, QTreeWidgetItem):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -637,16 +618,14 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def focusOutEvent(self, theEvent):
|
def focusOutEvent(self, event: QFocusEvent) -> None:
|
||||||
"""Clear the selection when the tree no longer has focus.
|
"""Clear the selection when the tree no longer has focus."""
|
||||||
"""
|
super().focusOutEvent(event)
|
||||||
super().focusOutEvent(theEvent)
|
|
||||||
self.clearSelection()
|
self.clearSelection()
|
||||||
return
|
return
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||||
"""Elide labels in the extra column.
|
"""Elide labels in the extra column."""
|
||||||
"""
|
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
newW = event.size().width()
|
newW = event.size().width()
|
||||||
oldW = event.oldSize().width()
|
oldW = event.oldSize().width()
|
||||||
@@ -665,18 +644,17 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@pyqtSlot("QModelIndex")
|
@pyqtSlot("QModelIndex")
|
||||||
def _treeItemClicked(self, mIndex):
|
def _treeItemClicked(self, index: QModelIndex) -> None:
|
||||||
"""The user clicked on an item in the tree.
|
"""The user clicked on an item in the tree."""
|
||||||
"""
|
if index.column() == self.C_MORE:
|
||||||
if mIndex.column() == self.C_MORE:
|
tHandle = index.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
|
||||||
tHandle = mIndex.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
|
sTitle = index.siblingAtColumn(self.C_DATA).data(self.D_TITLE)
|
||||||
sTitle = mIndex.siblingAtColumn(self.C_DATA).data(self.D_TITLE)
|
tipPos = self.mapToGlobal(self.visualRect(index).topRight())
|
||||||
tipPos = self.mapToGlobal(self.visualRect(mIndex).topRight())
|
|
||||||
self._popMetaBox(tipPos, tHandle, sTitle)
|
self._popMetaBox(tipPos, tHandle, sTitle)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _treeSelectionChange(self):
|
def _treeSelectionChange(self) -> None:
|
||||||
"""Extract the handle and line number of the currently selected
|
"""Extract the handle and line number of the currently selected
|
||||||
title, and send it to the tree meta panel.
|
title, and send it to the tree meta panel.
|
||||||
"""
|
"""
|
||||||
@@ -686,7 +664,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot("QTreeWidgetItem*", int)
|
@pyqtSlot("QTreeWidgetItem*", int)
|
||||||
def _treeDoubleClick(self, tItem, colNo):
|
def _treeDoubleClick(self, item: QTreeWidgetItem, column: int) -> None:
|
||||||
"""Extract the handle and line number of the title double-
|
"""Extract the handle and line number of the title double-
|
||||||
clicked, and send it to the main gui class for opening in the
|
clicked, and send it to the main gui class for opening in the
|
||||||
document editor.
|
document editor.
|
||||||
@@ -699,9 +677,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _populateTree(self, rootHandle):
|
def _populateTree(self, rootHandle: str | None) -> None:
|
||||||
"""Build the tree based on the project index.
|
"""Build the tree based on the project index."""
|
||||||
"""
|
|
||||||
self.clearContent()
|
self.clearContent()
|
||||||
tStart = time()
|
tStart = time()
|
||||||
logger.debug("Building novel tree for root item '%s'", rootHandle)
|
logger.debug("Building novel tree for root item '%s'", rootHandle)
|
||||||
@@ -728,9 +705,9 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _updateTreeItemValues(self, trItem, idxItem, tHandle, sTitle):
|
def _updateTreeItemValues(self, trItem: QTreeWidgetItem, idxItem: IndexHeading,
|
||||||
"""Set the tree item values from the index entry.
|
tHandle: str, sTitle: str) -> None:
|
||||||
"""
|
"""Set the tree item values from the index entry."""
|
||||||
iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
|
iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
|
||||||
hDec = SHARED.theme.getHeaderDecoration(iLevel)
|
hDec = SHARED.theme.getHeaderDecoration(iLevel)
|
||||||
|
|
||||||
@@ -750,9 +727,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _getLastColumnText(self, tHandle, sTitle):
|
def _getLastColumnText(self, tHandle: str, sTitle: str) -> tuple[str, str]:
|
||||||
"""Generate the text for the last column based on user settings.
|
"""Generate text for the last column based on user settings."""
|
||||||
"""
|
|
||||||
if self._lastCol == NovelTreeColumn.HIDDEN:
|
if self._lastCol == NovelTreeColumn.HIDDEN:
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|
||||||
@@ -777,14 +753,15 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
|
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|
||||||
def _popMetaBox(self, qPos, tHandle, sTitle):
|
def _popMetaBox(self, qPos: QPoint, tHandle: str, sTitle: str) -> None:
|
||||||
"""Show the novel meta data box.
|
"""Show the novel meta data box."""
|
||||||
"""
|
|
||||||
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
|
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
|
||||||
|
|
||||||
pIndex = SHARED.project.index
|
pIndex = SHARED.project.index
|
||||||
novIdx = pIndex.getItemHeader(tHandle, sTitle)
|
novIdx = pIndex.getItemHeader(tHandle, sTitle)
|
||||||
refTags = pIndex.getReferences(tHandle, sTitle)
|
refTags = pIndex.getReferences(tHandle, sTitle)
|
||||||
|
if not novIdx:
|
||||||
|
return
|
||||||
|
|
||||||
synopText = novIdx.synopsis
|
synopText = novIdx.synopsis
|
||||||
if synopText:
|
if synopText:
|
||||||
@@ -814,9 +791,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _appendMetaTag(refs, key, lines):
|
def _appendMetaTag(refs: dict, key: str, lines: list[str]) -> list[str]:
|
||||||
"""Generate a reference list for a given reference key.
|
"""Generate a reference list for a given reference key."""
|
||||||
"""
|
|
||||||
tags = ", ".join(refs.get(key, []))
|
tags = ", ".join(refs.get(key, []))
|
||||||
if tags:
|
if tags:
|
||||||
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
|
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
self.novelValue.updateList(includeAll=True)
|
self.novelValue.updateList(includeAll=True)
|
||||||
self.aRefresh.setIcon(SHARED.theme.getIcon("refresh"))
|
self.aRefresh.setIcon(SHARED.theme.getIcon("refresh"))
|
||||||
self.tbColumns.setIcon(SHARED.theme.getIcon("menu"))
|
self.tbColumns.setIcon(SHARED.theme.getIcon("menu"))
|
||||||
|
self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}")
|
||||||
return
|
return
|
||||||
|
|
||||||
def populateNovelList(self) -> None:
|
def populateNovelList(self) -> None:
|
||||||
|
|||||||
@@ -361,12 +361,13 @@ class GuiProjectToolBar(QWidget):
|
|||||||
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
||||||
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
||||||
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||||
|
buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}"
|
||||||
|
|
||||||
self.tbQuick.setStyleSheet(buttonStyle)
|
self.tbQuick.setStyleSheet(buttonStyleMenu)
|
||||||
self.tbMoveU.setStyleSheet(buttonStyle)
|
self.tbMoveU.setStyleSheet(buttonStyle)
|
||||||
self.tbMoveD.setStyleSheet(buttonStyle)
|
self.tbMoveD.setStyleSheet(buttonStyle)
|
||||||
self.tbAdd.setStyleSheet(buttonStyle)
|
self.tbAdd.setStyleSheet(buttonStyleMenu)
|
||||||
self.tbMore.setStyleSheet(buttonStyle)
|
self.tbMore.setStyleSheet(buttonStyleMenu)
|
||||||
|
|
||||||
self.tbQuick.setIcon(SHARED.theme.getIcon("bookmark"))
|
self.tbQuick.setIcon(SHARED.theme.getIcon("bookmark"))
|
||||||
self.tbMoveU.setIcon(SHARED.theme.getIcon("up"))
|
self.tbMoveU.setIcon(SHARED.theme.getIcon("up"))
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ class GuiSideBar(QWidget):
|
|||||||
self.outerBox.setSpacing(CONFIG.pxInt(4))
|
self.outerBox.setSpacing(CONFIG.pxInt(4))
|
||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("Ready: GuiSideBar")
|
logger.debug("Ready: GuiSideBar")
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from PyQt5.QtGui import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.enum import nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import NWConfigParser, minmax
|
from novelwriter.common import NWConfigParser, minmax
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
@@ -51,7 +51,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class GuiTheme:
|
class GuiTheme:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
|
|
||||||
self.iconCache = GuiIcons(self)
|
self.iconCache = GuiIcons(self)
|
||||||
|
|
||||||
@@ -130,6 +130,7 @@ class GuiTheme:
|
|||||||
self.getIcon = self.iconCache.getIcon
|
self.getIcon = self.iconCache.getIcon
|
||||||
self.getPixmap = self.iconCache.getPixmap
|
self.getPixmap = self.iconCache.getPixmap
|
||||||
self.getItemIcon = self.iconCache.getItemIcon
|
self.getItemIcon = self.iconCache.getItemIcon
|
||||||
|
self.getToggleIcon = self.iconCache.getToggleIcon
|
||||||
self.loadDecoration = self.iconCache.loadDecoration
|
self.loadDecoration = self.iconCache.loadDecoration
|
||||||
self.getHeaderDecoration = self.iconCache.getHeaderDecoration
|
self.getHeaderDecoration = self.iconCache.getHeaderDecoration
|
||||||
|
|
||||||
@@ -182,7 +183,7 @@ class GuiTheme:
|
|||||||
# Theme Methods
|
# Theme Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def loadTheme(self):
|
def loadTheme(self) -> bool:
|
||||||
"""Load the currently specified GUI theme."""
|
"""Load the currently specified GUI theme."""
|
||||||
guiTheme = CONFIG.guiTheme
|
guiTheme = CONFIG.guiTheme
|
||||||
if guiTheme not in self._availThemes:
|
if guiTheme not in self._availThemes:
|
||||||
@@ -269,7 +270,7 @@ class GuiTheme:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def loadSyntax(self):
|
def loadSyntax(self) -> bool:
|
||||||
"""Load the currently specified syntax highlighter theme."""
|
"""Load the currently specified syntax highlighter theme."""
|
||||||
guiSyntax = CONFIG.guiSyntax
|
guiSyntax = CONFIG.guiSyntax
|
||||||
if guiSyntax not in self._availSyntax:
|
if guiSyntax not in self._availSyntax:
|
||||||
@@ -363,7 +364,7 @@ class GuiTheme:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _setGuiFont(self):
|
def _setGuiFont(self) -> None:
|
||||||
"""Update the GUI's font style from settings."""
|
"""Update the GUI's font style from settings."""
|
||||||
theFont = QFont()
|
theFont = QFont()
|
||||||
fontDB = QFontDatabase()
|
fontDB = QFontDatabase()
|
||||||
@@ -395,9 +396,7 @@ class GuiTheme:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _parseColour(
|
def _parseColour(self, parser: NWConfigParser, section: str, name: str) -> list[int]:
|
||||||
self, parser: NWConfigParser, section: str, name: str
|
|
||||||
) -> list[int]:
|
|
||||||
"""Parse a colour value from a config string."""
|
"""Parse a colour value from a config string."""
|
||||||
if parser.has_option(section, name):
|
if parser.has_option(section, name):
|
||||||
values = parser.get(section, name).split(",")
|
values = parser.get(section, name).split(",")
|
||||||
@@ -414,9 +413,8 @@ class GuiTheme:
|
|||||||
result = [0, 0, 0]
|
result = [0, 0, 0]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _setPalette(
|
def _setPalette(self, parser: NWConfigParser, section: str,
|
||||||
self, parser: NWConfigParser, section: str, name: str, value: QPalette.ColorRole
|
name: str, value: QPalette.ColorRole) -> None:
|
||||||
):
|
|
||||||
"""Set a palette colour value from a config string."""
|
"""Set a palette colour value from a config string."""
|
||||||
self._guiPalette.setColor(
|
self._guiPalette.setColor(
|
||||||
value, QColor(*self._parseColour(parser, section, name))
|
value, QColor(*self._parseColour(parser, section, name))
|
||||||
@@ -447,14 +445,22 @@ class GuiIcons:
|
|||||||
ICON_KEYS = {
|
ICON_KEYS = {
|
||||||
# Project and GUI Icons
|
# Project and GUI Icons
|
||||||
"novelwriter", "alert_error", "alert_info", "alert_question", "alert_warn",
|
"novelwriter", "alert_error", "alert_info", "alert_question", "alert_warn",
|
||||||
"build_excluded", "build_filtered", "build_included", "cls_archive", "cls_character",
|
"build_excluded", "build_filtered", "build_included", "proj_chapter", "proj_details",
|
||||||
"cls_custom", "cls_entity", "cls_none", "cls_novel", "cls_object", "cls_plot",
|
"proj_document", "proj_folder", "proj_note", "proj_nwx", "proj_section", "proj_scene",
|
||||||
"cls_timeline", "cls_trash", "cls_world", "proj_chapter", "proj_details", "proj_document",
|
"proj_stats", "proj_title", "status_idle", "status_lang", "status_lines", "status_stats",
|
||||||
"proj_folder", "proj_note", "proj_nwx", "proj_section", "proj_scene", "proj_stats",
|
"status_time", "view_build", "view_editor", "view_novel", "view_outline",
|
||||||
"proj_title", "search_cancel", "search_case", "search_loop", "search_preserve",
|
|
||||||
"search_project", "search_regex", "search_word", "status_idle", "status_lang",
|
# Class Icons
|
||||||
"status_lines", "status_stats", "status_time", "view_build", "view_editor", "view_novel",
|
"cls_archive", "cls_character", "cls_custom", "cls_entity", "cls_none", "cls_novel",
|
||||||
"view_outline",
|
"cls_object", "cls_plot", "cls_timeline", "cls_trash", "cls_world",
|
||||||
|
|
||||||
|
# Search Icons
|
||||||
|
"search_cancel", "search_case", "search_loop", "search_preserve", "search_project",
|
||||||
|
"search_regex", "search_word",
|
||||||
|
|
||||||
|
# Format Icons
|
||||||
|
"fmt_bold", "fmt_italic", "fmt_mode-md", "fmt_mode-sc", "fmt_strike", "fmt_subscript",
|
||||||
|
"fmt_superscript", "fmt_underline",
|
||||||
|
|
||||||
# General Button Icons
|
# General Button Icons
|
||||||
"add", "backward", "bookmark", "browse", "checked", "close", "cross", "down", "edit",
|
"add", "backward", "bookmark", "browse", "checked", "close", "cross", "down", "edit",
|
||||||
@@ -469,21 +475,27 @@ class GuiIcons:
|
|||||||
"deco_doc_h0", "deco_doc_h1", "deco_doc_h2", "deco_doc_h3", "deco_doc_h4", "deco_doc_more",
|
"deco_doc_h0", "deco_doc_h1", "deco_doc_h2", "deco_doc_h3", "deco_doc_h4", "deco_doc_more",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TOGGLE_ICON_KEYS = {
|
||||||
|
"sticky": ("sticky-on", "sticky-off"),
|
||||||
|
"bullet": ("bullet-on", "bullet-off"),
|
||||||
|
"fmt_mode": ("fmt_mode-sc", "fmt_mode-md"),
|
||||||
|
}
|
||||||
|
|
||||||
IMAGE_MAP = {
|
IMAGE_MAP = {
|
||||||
"wiz-back": "wizard-back.jpg",
|
"wiz-back": "wizard-back.jpg",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, mainTheme):
|
def __init__(self, mainTheme: GuiTheme) -> None:
|
||||||
|
|
||||||
self.mainTheme = mainTheme
|
self.mainTheme = mainTheme
|
||||||
|
|
||||||
# Storage
|
# Storage
|
||||||
self._qIcons = {}
|
self._qIcons: dict[str, QIcon] = {}
|
||||||
self._themeMap = {}
|
self._themeMap: dict[str, Path] = {}
|
||||||
self._headerDec = []
|
self._headerDec: list[QPixmap] = []
|
||||||
self._confName = "icons.conf"
|
|
||||||
|
|
||||||
# Icon Theme Path
|
# Icon Theme Path
|
||||||
|
self._confName = "icons.conf"
|
||||||
self._iconPath = CONFIG.assetPath("icons")
|
self._iconPath = CONFIG.assetPath("icons")
|
||||||
|
|
||||||
# Icon Theme Meta
|
# Icon Theme Meta
|
||||||
@@ -501,7 +513,7 @@ class GuiIcons:
|
|||||||
# Actions
|
# Actions
|
||||||
##
|
##
|
||||||
|
|
||||||
def loadTheme(self, iconTheme):
|
def loadTheme(self, iconTheme: str) -> bool:
|
||||||
"""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.
|
||||||
@@ -575,52 +587,60 @@ class GuiIcons:
|
|||||||
# Access Functions
|
# Access Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def loadDecoration(self, decoKey, pxW=None, pxH=None):
|
def loadDecoration(self, name: str, w: int | None = None, h: int | None = None) -> QPixmap:
|
||||||
"""Load graphical decoration element based on the decoration
|
"""Load graphical decoration element based on the decoration
|
||||||
map or the icon map. This function always returns a QPixmap.
|
map or the icon map. This function always returns a QPixmap.
|
||||||
"""
|
"""
|
||||||
if decoKey in self._themeMap:
|
if name in self._themeMap:
|
||||||
imgPath = self._themeMap[decoKey]
|
imgPath = self._themeMap[name]
|
||||||
elif decoKey in self.IMAGE_MAP:
|
elif name in self.IMAGE_MAP:
|
||||||
imgPath = CONFIG.assetPath("images") / self.IMAGE_MAP[decoKey]
|
imgPath = CONFIG.assetPath("images") / self.IMAGE_MAP[name]
|
||||||
else:
|
else:
|
||||||
logger.error("Decoration with name '%s' does not exist", decoKey)
|
logger.error("Decoration with name '%s' does not exist", name)
|
||||||
return QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
if not imgPath.is_file():
|
if not imgPath.is_file():
|
||||||
logger.error("Asset not found: %s", imgPath)
|
logger.error("Asset not found: %s", imgPath)
|
||||||
return QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
theDeco = QPixmap(str(imgPath))
|
pixmap = QPixmap(str(imgPath))
|
||||||
if pxW is not None and pxH is not None:
|
if w is not None and h is not None:
|
||||||
return theDeco.scaled(pxW, pxH, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
return pixmap.scaled(w, h, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||||
elif pxW is None and pxH is not None:
|
elif w is None and h is not None:
|
||||||
return theDeco.scaledToHeight(pxH, Qt.SmoothTransformation)
|
return pixmap.scaledToHeight(h, Qt.SmoothTransformation)
|
||||||
elif pxW is not None and pxH is None:
|
elif w is not None and h is None:
|
||||||
return theDeco.scaledToWidth(pxW, Qt.SmoothTransformation)
|
return pixmap.scaledToWidth(w, Qt.SmoothTransformation)
|
||||||
|
|
||||||
return theDeco
|
return pixmap
|
||||||
|
|
||||||
def getIcon(self, iconKey):
|
def getIcon(self, name: str) -> QIcon:
|
||||||
"""Return an icon from the icon buffer. If it doesn't exist,
|
"""Return an icon from the icon buffer, or load it."""
|
||||||
return, load it, and if it still doesn't exist, return an empty
|
if name in self._qIcons:
|
||||||
icon.
|
return self._qIcons[name]
|
||||||
"""
|
|
||||||
if iconKey in self._qIcons:
|
|
||||||
return self._qIcons[iconKey]
|
|
||||||
else:
|
else:
|
||||||
qIcon = self._loadIcon(iconKey)
|
icon = self._loadIcon(name)
|
||||||
self._qIcons[iconKey] = qIcon
|
self._qIcons[name] = icon
|
||||||
return qIcon
|
return icon
|
||||||
|
|
||||||
def getPixmap(self, iconKey, iconSize):
|
def getToggleIcon(self, name: str, size: tuple[int, int]) -> QIcon:
|
||||||
|
"""Return a toggle icon from the icon buffer. or load it."""
|
||||||
|
if name in self.TOGGLE_ICON_KEYS:
|
||||||
|
pOne = self.getPixmap(self.TOGGLE_ICON_KEYS[name][0], size)
|
||||||
|
pTwo = self.getPixmap(self.TOGGLE_ICON_KEYS[name][1], size)
|
||||||
|
icon = QIcon()
|
||||||
|
icon.addPixmap(pOne, QIcon.Normal, QIcon.On)
|
||||||
|
icon.addPixmap(pTwo, QIcon.Normal, QIcon.Off)
|
||||||
|
return icon
|
||||||
|
return QIcon()
|
||||||
|
|
||||||
|
def getPixmap(self, name: str, size: tuple[int, int]) -> QPixmap:
|
||||||
"""Return an icon from the icon buffer as a QPixmap. If it
|
"""Return an icon from the icon buffer as a QPixmap. If it
|
||||||
doesn't exist, return an empty QPixmap.
|
doesn't exist, return an empty QPixmap.
|
||||||
"""
|
"""
|
||||||
qIcon = self.getIcon(iconKey)
|
return self.getIcon(name).pixmap(size[0], size[1], QIcon.Normal)
|
||||||
return qIcon.pixmap(iconSize[0], iconSize[1], QIcon.Normal)
|
|
||||||
|
|
||||||
def getItemIcon(self, tType, tClass, tLayout, hLevel="H0"):
|
def getItemIcon(self, tType: nwItemType, tClass: nwItemClass,
|
||||||
|
tLayout: nwItemLayout, hLevel: str = "H0") -> QIcon:
|
||||||
"""Get the correct icon for a project item based on type, class
|
"""Get the correct icon for a project item based on type, class
|
||||||
and header level
|
and header level
|
||||||
"""
|
"""
|
||||||
@@ -647,17 +667,16 @@ class GuiIcons:
|
|||||||
|
|
||||||
return self.getIcon(iconName)
|
return self.getIcon(iconName)
|
||||||
|
|
||||||
def getHeaderDecoration(self, hLevel):
|
def getHeaderDecoration(self, hLevel: int) -> QPixmap:
|
||||||
"""Get the decoration for a specific header level.
|
"""Get the decoration for a specific header level."""
|
||||||
"""
|
|
||||||
if not self._headerDec:
|
if not self._headerDec:
|
||||||
iPx = self.mainTheme.baseIconSize
|
iPx = self.mainTheme.baseIconSize
|
||||||
self._headerDec = [
|
self._headerDec = [
|
||||||
self.loadDecoration("deco_doc_h0", pxH=iPx),
|
self.loadDecoration("deco_doc_h0", h=iPx),
|
||||||
self.loadDecoration("deco_doc_h1", pxH=iPx),
|
self.loadDecoration("deco_doc_h1", h=iPx),
|
||||||
self.loadDecoration("deco_doc_h2", pxH=iPx),
|
self.loadDecoration("deco_doc_h2", h=iPx),
|
||||||
self.loadDecoration("deco_doc_h3", pxH=iPx),
|
self.loadDecoration("deco_doc_h3", h=iPx),
|
||||||
self.loadDecoration("deco_doc_h4", pxH=iPx),
|
self.loadDecoration("deco_doc_h4", h=iPx),
|
||||||
]
|
]
|
||||||
return self._headerDec[minmax(hLevel, 0, 4)]
|
return self._headerDec[minmax(hLevel, 0, 4)]
|
||||||
|
|
||||||
@@ -665,27 +684,27 @@ class GuiIcons:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _loadIcon(self, iconKey):
|
def _loadIcon(self, name: str) -> QIcon:
|
||||||
"""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.
|
||||||
"""
|
"""
|
||||||
if iconKey not in self.ICON_KEYS:
|
if name not in self.ICON_KEYS:
|
||||||
logger.error("Requested unknown icon name '%s'", iconKey)
|
logger.error("Requested unknown icon name '%s'", name)
|
||||||
return QIcon()
|
return QIcon()
|
||||||
|
|
||||||
# If we just want the app icons, return right away
|
# If we just want the app icons, return right away
|
||||||
if iconKey == "novelwriter":
|
if name == "novelwriter":
|
||||||
return QIcon(str(self._iconPath / "novelwriter.svg"))
|
return QIcon(str(self._iconPath / "novelwriter.svg"))
|
||||||
elif iconKey == "proj_nwx":
|
elif name == "proj_nwx":
|
||||||
return QIcon(str(self._iconPath / "x-novelwriter-project.svg"))
|
return QIcon(str(self._iconPath / "x-novelwriter-project.svg"))
|
||||||
|
|
||||||
# Otherwise, we load from the theme folder
|
# Otherwise, we load from the theme folder
|
||||||
if iconKey in self._themeMap:
|
if name in self._themeMap:
|
||||||
logger.debug("Loading: %s", self._themeMap[iconKey].name)
|
logger.debug("Loading: %s", self._themeMap[name].name)
|
||||||
return QIcon(str(self._themeMap[iconKey]))
|
return QIcon(str(self._themeMap[name]))
|
||||||
|
|
||||||
# If we didn't find one, give up and return an empty icon
|
# If we didn't find one, give up and return an empty icon
|
||||||
logger.warning("Did not load an icon for '%s'", iconKey)
|
logger.warning("Did not load an icon for '%s'", name)
|
||||||
|
|
||||||
return QIcon()
|
return QIcon()
|
||||||
|
|
||||||
@@ -696,9 +715,8 @@ class GuiIcons:
|
|||||||
# Module Functions
|
# Module Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def _loadInternalName(confParser, confFile):
|
def _loadInternalName(confParser: NWConfigParser, confFile: str | Path) -> str:
|
||||||
"""Open a conf file and read the 'name' setting.
|
"""Open a conf file and read the 'name' setting."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
with open(confFile, mode="r", encoding="utf-8") as inFile:
|
with open(confFile, mode="r", encoding="utf-8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ from novelwriter.tools.writingstats import GuiWritingStats
|
|||||||
from novelwriter.core.coretools import ProjectBuilder
|
from novelwriter.core.coretools import ProjectBuilder
|
||||||
|
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
nwDocAction, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView
|
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView
|
||||||
)
|
)
|
||||||
from novelwriter.common import getGuiItem, hexToInt
|
from novelwriter.common import getGuiItem, hexToInt
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
@@ -243,6 +243,11 @@ class GuiMain(QMainWindow):
|
|||||||
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
|
||||||
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
SHARED.spellLanguageChanged.connect(self.mainStatus.setLanguage)
|
||||||
|
|
||||||
|
self.mainMenu.requestDocAction.connect(self._passDocumentAction)
|
||||||
|
self.mainMenu.requestDocInsert.connect(self._passDocumentInsert)
|
||||||
|
self.mainMenu.requestDocInsertText.connect(self._passDocumentInsert)
|
||||||
|
self.mainMenu.requestDocKeyWordInsert.connect(self.docEditor.insertKeyWord)
|
||||||
|
|
||||||
self.sideBar.viewChangeRequested.connect(self._changeView)
|
self.sideBar.viewChangeRequested.connect(self._changeView)
|
||||||
|
|
||||||
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||||
@@ -267,6 +272,8 @@ class GuiMain(QMainWindow):
|
|||||||
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
|
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
|
||||||
self.docEditor.statusMessage.connect(self.mainStatus.setStatusMessage)
|
self.docEditor.statusMessage.connect(self.mainStatus.setStatusMessage)
|
||||||
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
|
self.docEditor.spellCheckStateChanged.connect(self.mainMenu.setSpellCheckState)
|
||||||
|
self.docEditor.closeDocumentRequest.connect(self.closeDocEditor)
|
||||||
|
self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode)
|
||||||
|
|
||||||
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
|
||||||
|
|
||||||
@@ -733,20 +740,6 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def passDocumentAction(self, action: nwDocAction) -> None:
|
|
||||||
"""Pass on document action to the document viewer if it has
|
|
||||||
focus, or pass it to the document editor if it or any of
|
|
||||||
its child widgets have focus. If neither has focus, ignore the
|
|
||||||
action.
|
|
||||||
"""
|
|
||||||
if self.docViewer.hasFocus():
|
|
||||||
self.docViewer.docAction(action)
|
|
||||||
elif self.docEditor.hasFocus():
|
|
||||||
self.docEditor.docAction(action)
|
|
||||||
else:
|
|
||||||
logger.debug("Action cancelled as neither editor nor viewer has focus")
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Tree Item Actions
|
# Tree Item Actions
|
||||||
##
|
##
|
||||||
@@ -1119,12 +1112,6 @@ class GuiMain(QMainWindow):
|
|||||||
self.outlineView.setTreeFocus()
|
self.outlineView.setTreeFocus()
|
||||||
return
|
return
|
||||||
|
|
||||||
def closeDocEditor(self) -> None:
|
|
||||||
"""Close the document editor. This does not hide the editor."""
|
|
||||||
self.closeDocument()
|
|
||||||
SHARED.project.data.setLastHandle(None, "editor")
|
|
||||||
return
|
|
||||||
|
|
||||||
def closeDocViewer(self, byUser: bool = True) -> bool:
|
def closeDocViewer(self, byUser: bool = True) -> bool:
|
||||||
"""Close the document view panel."""
|
"""Close the document view panel."""
|
||||||
self.docViewer.clearViewer()
|
self.docViewer.clearViewer()
|
||||||
@@ -1139,13 +1126,44 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return not self.splitView.isVisible()
|
return not self.splitView.isVisible()
|
||||||
|
|
||||||
def toggleFocusMode(self) -> bool:
|
def toggleFullScreenMode(self) -> None:
|
||||||
|
"""Toggle full screen mode"""
|
||||||
|
self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Events
|
||||||
|
##
|
||||||
|
|
||||||
|
def closeEvent(self, event: QCloseEvent):
|
||||||
|
"""Capture the closing event of the GUI and call the close
|
||||||
|
function to handle all the close process steps.
|
||||||
|
"""
|
||||||
|
if self.closeMain():
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Public Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def closeDocEditor(self) -> None:
|
||||||
|
"""Close the document editor. This does not hide the editor."""
|
||||||
|
self.closeDocument()
|
||||||
|
SHARED.project.data.setLastHandle(None, "editor")
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def toggleFocusMode(self) -> None:
|
||||||
"""Handle toggle focus mode. The Main GUI Focus Mode hides tree,
|
"""Handle toggle focus mode. The Main GUI Focus Mode hides tree,
|
||||||
view, statusbar and menu.
|
view, statusbar and menu.
|
||||||
"""
|
"""
|
||||||
if self.docEditor.docHandle is None:
|
if self.docEditor.docHandle is None:
|
||||||
logger.error("No document open, so not activating Focus Mode")
|
logger.error("No document open, so not activating Focus Mode")
|
||||||
return False
|
return
|
||||||
|
|
||||||
self.isFocusMode = not self.isFocusMode
|
self.isFocusMode = not self.isFocusMode
|
||||||
if self.isFocusMode:
|
if self.isFocusMode:
|
||||||
@@ -1169,11 +1187,165 @@ class GuiMain(QMainWindow):
|
|||||||
elif self.docViewer.docHandle is not None:
|
elif self.docViewer.docHandle is not None:
|
||||||
self.splitView.setVisible(True)
|
self.splitView.setVisible(True)
|
||||||
|
|
||||||
return True
|
return
|
||||||
|
|
||||||
def toggleFullScreenMode(self) -> None:
|
##
|
||||||
"""Toggle full screen mode"""
|
# Private Slots
|
||||||
self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
|
##
|
||||||
|
|
||||||
|
@pyqtSlot(str, nwDocMode)
|
||||||
|
def _followTag(self, tag: str, mode: nwDocMode) -> None:
|
||||||
|
"""Follow a tag after user interaction with a link."""
|
||||||
|
tHandle, sTitle = self._getTagSource(tag)
|
||||||
|
if tHandle is not None:
|
||||||
|
if mode == nwDocMode.EDIT:
|
||||||
|
self.openDocument(tHandle)
|
||||||
|
elif mode == nwDocMode.VIEW:
|
||||||
|
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(str, nwDocMode, str, bool)
|
||||||
|
def _openDocument(self, tHandle: str, mode: nwDocMode, sTitle: str, setFocus: bool) -> None:
|
||||||
|
"""Handle an open document request."""
|
||||||
|
if tHandle is not None:
|
||||||
|
if mode == nwDocMode.EDIT:
|
||||||
|
tLine = None
|
||||||
|
hItem = SHARED.project.index.getItemHeader(tHandle, sTitle)
|
||||||
|
if hItem is not None:
|
||||||
|
tLine = hItem.line
|
||||||
|
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
|
||||||
|
elif mode == nwDocMode.VIEW:
|
||||||
|
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(nwView)
|
||||||
|
def _changeView(self, view: nwView) -> None:
|
||||||
|
"""Handle the requested change of view from the GuiViewBar."""
|
||||||
|
if view == nwView.EDITOR:
|
||||||
|
# Only change the main stack, but not the project stack
|
||||||
|
self.mainStack.setCurrentWidget(self.splitMain)
|
||||||
|
|
||||||
|
elif view == nwView.PROJECT:
|
||||||
|
self.mainStack.setCurrentWidget(self.splitMain)
|
||||||
|
self.projStack.setCurrentWidget(self.projView)
|
||||||
|
|
||||||
|
elif view == nwView.NOVEL:
|
||||||
|
self.mainStack.setCurrentWidget(self.splitMain)
|
||||||
|
self.projStack.setCurrentWidget(self.novelView)
|
||||||
|
|
||||||
|
elif view == nwView.OUTLINE:
|
||||||
|
self.mainStack.setCurrentWidget(self.outlineView)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(nwDocAction)
|
||||||
|
def _passDocumentAction(self, action: nwDocAction) -> None:
|
||||||
|
"""Pass on a document action to the document viewer if it has
|
||||||
|
focus, or pass it to the document editor if it or any of its
|
||||||
|
child widgets have focus. If neither has focus, ignore it.
|
||||||
|
"""
|
||||||
|
if self.docViewer.hasFocus():
|
||||||
|
self.docViewer.docAction(action)
|
||||||
|
elif self.docEditor.hasFocus():
|
||||||
|
self.docEditor.docAction(action)
|
||||||
|
else:
|
||||||
|
logger.debug("Action cancelled as neither editor nor viewer has focus")
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
@pyqtSlot(nwDocInsert)
|
||||||
|
def _passDocumentInsert(self, content: str | nwDocInsert) -> None:
|
||||||
|
"""Pass on a document insert action to the document editor if it
|
||||||
|
has focus. If not, ignore it.
|
||||||
|
"""
|
||||||
|
if self.docEditor.hasFocus():
|
||||||
|
self.docEditor.insertText(content)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _timeTick(self) -> None:
|
||||||
|
"""Process time tick of the main timer."""
|
||||||
|
if not SHARED.hasProject:
|
||||||
|
return
|
||||||
|
currTime = time()
|
||||||
|
editIdle = currTime - self.docEditor.lastActive > CONFIG.userIdleTime
|
||||||
|
userIdle = qApp.applicationState() != Qt.ApplicationActive
|
||||||
|
self.mainStatus.setUserIdle(editIdle or userIdle)
|
||||||
|
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
||||||
|
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _autoSaveProject(self) -> None:
|
||||||
|
"""Autosave of the project. This is a timer-activated slot."""
|
||||||
|
doSave = SHARED.hasProject
|
||||||
|
doSave &= SHARED.project.projChanged
|
||||||
|
doSave &= SHARED.project.storage.isOpen()
|
||||||
|
if doSave:
|
||||||
|
logger.debug("Autosaving project")
|
||||||
|
self.saveProject(autoSave=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _autoSaveDocument(self) -> None:
|
||||||
|
"""Autosave of the document. This is a timer-activated slot."""
|
||||||
|
if SHARED.hasProject and self.docEditor.docChanged:
|
||||||
|
logger.debug("Autosaving document")
|
||||||
|
self.saveDocument()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _updateStatusWordCount(self) -> None:
|
||||||
|
"""Update the word count on the status bar."""
|
||||||
|
if not SHARED.hasProject:
|
||||||
|
self.mainStatus.setProjectStats(0, 0)
|
||||||
|
|
||||||
|
SHARED.project.updateWordCounts()
|
||||||
|
if CONFIG.incNotesWCount:
|
||||||
|
iTotal = sum(SHARED.project.data.initCounts)
|
||||||
|
cTotal = sum(SHARED.project.data.currCounts)
|
||||||
|
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||||
|
else:
|
||||||
|
iNovel, _ = SHARED.project.data.initCounts
|
||||||
|
cNovel, _ = SHARED.project.data.currCounts
|
||||||
|
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _keyPressReturn(self) -> None:
|
||||||
|
"""Forward the return/enter keypress to the function that opens
|
||||||
|
the currently selected item.
|
||||||
|
"""
|
||||||
|
self.openSelectedItem()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _keyPressEscape(self) -> None:
|
||||||
|
"""Process escape keypress in the main window."""
|
||||||
|
if self.docEditor.docSearch.isVisible():
|
||||||
|
self.docEditor.closeSearch()
|
||||||
|
elif self.isFocusMode:
|
||||||
|
self.toggleFocusMode()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def _mainStackChanged(self, index: int) -> None:
|
||||||
|
"""Process main window tab change."""
|
||||||
|
if index == self.idxOutlineView:
|
||||||
|
if SHARED.hasProject:
|
||||||
|
self.outlineView.refreshTree()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(int)
|
||||||
|
def _projStackChanged(self, index: int) -> None:
|
||||||
|
"""Process project view tab change."""
|
||||||
|
sHandle = None
|
||||||
|
if index == self.idxProjView:
|
||||||
|
sHandle = self.projView.getSelectedHandle()
|
||||||
|
elif index == self.idxNovelView:
|
||||||
|
sHandle, _ = self.novelView.getSelectedHandle()
|
||||||
|
self.itemDetails.updateViewBox(sHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -1328,153 +1500,4 @@ class GuiMain(QMainWindow):
|
|||||||
return None, None
|
return None, None
|
||||||
return tHandle, sTitle
|
return tHandle, sTitle
|
||||||
|
|
||||||
##
|
|
||||||
# Events
|
|
||||||
##
|
|
||||||
|
|
||||||
def closeEvent(self, event: QCloseEvent):
|
|
||||||
"""Capture the closing event of the GUI and call the close
|
|
||||||
function to handle all the close process steps.
|
|
||||||
"""
|
|
||||||
if self.closeMain():
|
|
||||||
event.accept()
|
|
||||||
else:
|
|
||||||
event.ignore()
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Private Slots
|
|
||||||
##
|
|
||||||
|
|
||||||
@pyqtSlot(str, nwDocMode)
|
|
||||||
def _followTag(self, tag: str, mode: nwDocMode) -> None:
|
|
||||||
"""Follow a tag after user interaction with a link."""
|
|
||||||
tHandle, sTitle = self._getTagSource(tag)
|
|
||||||
if tHandle is not None:
|
|
||||||
if mode == nwDocMode.EDIT:
|
|
||||||
self.openDocument(tHandle)
|
|
||||||
elif mode == nwDocMode.VIEW:
|
|
||||||
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(str, nwDocMode, str, bool)
|
|
||||||
def _openDocument(self, tHandle: str, mode: nwDocMode, sTitle: str, setFocus: bool) -> None:
|
|
||||||
"""Handle an open document request."""
|
|
||||||
if tHandle is not None:
|
|
||||||
if mode == nwDocMode.EDIT:
|
|
||||||
tLine = None
|
|
||||||
hItem = SHARED.project.index.getItemHeader(tHandle, sTitle)
|
|
||||||
if hItem is not None:
|
|
||||||
tLine = hItem.line
|
|
||||||
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
|
|
||||||
elif mode == nwDocMode.VIEW:
|
|
||||||
self.viewDocument(tHandle=tHandle, sTitle=sTitle)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(nwView)
|
|
||||||
def _changeView(self, view: nwView) -> None:
|
|
||||||
"""Handle the requested change of view from the GuiViewBar."""
|
|
||||||
if view == nwView.EDITOR:
|
|
||||||
# Only change the main stack, but not the project stack
|
|
||||||
self.mainStack.setCurrentWidget(self.splitMain)
|
|
||||||
|
|
||||||
elif view == nwView.PROJECT:
|
|
||||||
self.mainStack.setCurrentWidget(self.splitMain)
|
|
||||||
self.projStack.setCurrentWidget(self.projView)
|
|
||||||
|
|
||||||
elif view == nwView.NOVEL:
|
|
||||||
self.mainStack.setCurrentWidget(self.splitMain)
|
|
||||||
self.projStack.setCurrentWidget(self.novelView)
|
|
||||||
|
|
||||||
elif view == nwView.OUTLINE:
|
|
||||||
self.mainStack.setCurrentWidget(self.outlineView)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _timeTick(self) -> None:
|
|
||||||
"""Process time tick of the main timer."""
|
|
||||||
if not SHARED.hasProject:
|
|
||||||
return
|
|
||||||
currTime = time()
|
|
||||||
editIdle = currTime - self.docEditor.lastActive > CONFIG.userIdleTime
|
|
||||||
userIdle = qApp.applicationState() != Qt.ApplicationActive
|
|
||||||
self.mainStatus.setUserIdle(editIdle or userIdle)
|
|
||||||
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
|
||||||
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _autoSaveProject(self) -> None:
|
|
||||||
"""Autosave of the project. This is a timer-activated slot."""
|
|
||||||
doSave = SHARED.hasProject
|
|
||||||
doSave &= SHARED.project.projChanged
|
|
||||||
doSave &= SHARED.project.storage.isOpen()
|
|
||||||
if doSave:
|
|
||||||
logger.debug("Autosaving project")
|
|
||||||
self.saveProject(autoSave=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _autoSaveDocument(self) -> None:
|
|
||||||
"""Autosave of the document. This is a timer-activated slot."""
|
|
||||||
if SHARED.hasProject and self.docEditor.docChanged:
|
|
||||||
logger.debug("Autosaving document")
|
|
||||||
self.saveDocument()
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _updateStatusWordCount(self) -> None:
|
|
||||||
"""Update the word count on the status bar."""
|
|
||||||
if not SHARED.hasProject:
|
|
||||||
self.mainStatus.setProjectStats(0, 0)
|
|
||||||
|
|
||||||
SHARED.project.updateWordCounts()
|
|
||||||
if CONFIG.incNotesWCount:
|
|
||||||
iTotal = sum(SHARED.project.data.initCounts)
|
|
||||||
cTotal = sum(SHARED.project.data.currCounts)
|
|
||||||
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
|
||||||
else:
|
|
||||||
iNovel, _ = SHARED.project.data.initCounts
|
|
||||||
cNovel, _ = SHARED.project.data.currCounts
|
|
||||||
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _keyPressReturn(self) -> None:
|
|
||||||
"""Forward the return/enter keypress to the function that opens
|
|
||||||
the currently selected item.
|
|
||||||
"""
|
|
||||||
self.openSelectedItem()
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _keyPressEscape(self) -> None:
|
|
||||||
"""Process escape keypress in the main window."""
|
|
||||||
if self.docEditor.docSearch.isVisible():
|
|
||||||
self.docEditor.closeSearch()
|
|
||||||
elif self.isFocusMode:
|
|
||||||
self.toggleFocusMode()
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def _mainStackChanged(self, index: int) -> None:
|
|
||||||
"""Process main window tab change."""
|
|
||||||
if index == self.idxOutlineView:
|
|
||||||
if SHARED.hasProject:
|
|
||||||
self.outlineView.refreshTree()
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def _projStackChanged(self, index: int) -> None:
|
|
||||||
"""Process project view tab change."""
|
|
||||||
sHandle = None
|
|
||||||
if index == self.idxProjView:
|
|
||||||
sHandle = self.projView.getSelectedHandle()
|
|
||||||
elif index == self.idxNovelView:
|
|
||||||
sHandle, _ = self.novelView.getSelectedHandle()
|
|
||||||
self.itemDetails.updateViewBox(sHandle)
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class GuiMain
|
# END Class GuiMain
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ useridletime = 300
|
|||||||
|
|
||||||
[State]
|
[State]
|
||||||
showrefpanel = True
|
showrefpanel = True
|
||||||
|
showedittoolbar = False
|
||||||
|
useshortcodes = False
|
||||||
viewcomments = True
|
viewcomments = True
|
||||||
viewsynopsis = True
|
viewsynopsis = True
|
||||||
searchcase = False
|
searchcase = False
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ useridletime = 300
|
|||||||
|
|
||||||
[State]
|
[State]
|
||||||
showrefpanel = True
|
showrefpanel = True
|
||||||
|
showedittoolbar = False
|
||||||
|
useshortcodes = False
|
||||||
viewcomments = True
|
viewcomments = True
|
||||||
viewsynopsis = True
|
viewsynopsis = True
|
||||||
searchcase = False
|
searchcase = False
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ def testCoreOptions_SetGet(mockGUI):
|
|||||||
assert theOpts.getFloat("GuiProjectDetails", "mockItem", None) is None
|
assert theOpts.getFloat("GuiProjectDetails", "mockItem", None) is None
|
||||||
assert theOpts.getBool("GuiProjectDetails", "clearDouble", None) is True
|
assert theOpts.getBool("GuiProjectDetails", "clearDouble", None) is True
|
||||||
assert theOpts.getBool("GuiProjectDetails", "mockItem", None) is None
|
assert theOpts.getBool("GuiProjectDetails", "mockItem", None) is None
|
||||||
assert theOpts.getEnum("GuiNovelView", "lastCol", NovelTreeColumn, None) == nwColHidden
|
assert theOpts.getEnum("GuiNovelView", "lastCol", NovelTreeColumn, nwColHidden) == nwColHidden
|
||||||
|
|
||||||
# Get from non-existent groups
|
# Get from non-existent groups
|
||||||
assert theOpts.getValue("SomeGroup", "mockItem", None) is None
|
assert theOpts.getValue("SomeGroup", "mockItem", None) is None
|
||||||
|
|||||||
@@ -24,15 +24,15 @@ import pytest
|
|||||||
from tools import C, buildTestProject
|
from tools import C, buildTestProject
|
||||||
from mocked import causeOSError
|
from mocked import causeOSError
|
||||||
|
|
||||||
from PyQt5.QtCore import QThreadPool, Qt
|
|
||||||
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
||||||
|
from PyQt5.QtCore import QThreadPool, Qt
|
||||||
from PyQt5.QtWidgets import QAction, qApp
|
from PyQt5.QtWidgets import QAction, qApp
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout, nwTrinary, nwWidget
|
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout, nwTrinary, nwWidget
|
||||||
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, GuiDocToolBar
|
||||||
|
|
||||||
KEY_DELAY = 1
|
KEY_DELAY = 1
|
||||||
|
|
||||||
@@ -214,9 +214,8 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
buildTestProject(nwGUI, projPath)
|
buildTestProject(nwGUI, projPath)
|
||||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
theDoc = nwGUI.docEditor.document()
|
theDoc = nwGUI.docEditor.document()
|
||||||
|
|
||||||
# Select/Cut/Copy/Paste/Undo/Redo
|
# Select/Cut/Copy/Paste/Undo/Redo
|
||||||
@@ -228,7 +227,7 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) is True
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
theCursor = nwGUI.docEditor.textCursor()
|
||||||
assert theCursor.hasSelection() is True
|
assert theCursor.hasSelection() is True
|
||||||
assert theCursor.selectedText() == theText.replace("\n", "\u2029")
|
assert theCursor.selectedText() == text.replace("\n", "\u2029")
|
||||||
theCursor.clearSelection()
|
theCursor.clearSelection()
|
||||||
|
|
||||||
# Select Paragraph
|
# Select Paragraph
|
||||||
@@ -239,7 +238,7 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
assert theCursor.selectedText() == ipsumText[1]
|
assert theCursor.selectedText() == ipsumText[1]
|
||||||
|
|
||||||
# Cut Selected Text
|
# Cut Selected Text
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(1000)
|
nwGUI.docEditor.setCursorPosition(1000)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA) is True
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.CUT) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.CUT) is True
|
||||||
@@ -254,10 +253,10 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
|
|
||||||
# Paste Back In
|
# Paste Back In
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.PASTE) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.PASTE) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Copy Next Paragraph
|
# Copy Next Paragraph
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(1500)
|
nwGUI.docEditor.setCursorPosition(1500)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA) is True
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is True
|
||||||
@@ -279,84 +278,132 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
# Emphasis/Undo/Redo
|
# Emphasis/Undo/Redo
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % ipsumText[0]
|
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
# Emphasis
|
# Emphasis
|
||||||
nwGUI.docEditor.setCursorPosition(50)
|
nwGUI.docEditor.setCursorPosition(50)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.EMPH) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.EMPH) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "_consectetur_")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "_consectetur_")
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Strong
|
# Strong
|
||||||
nwGUI.docEditor.setCursorPosition(50)
|
nwGUI.docEditor.setCursorPosition(50)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.STRONG) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.STRONG) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "**consectetur**")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "**consectetur**")
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Strikeout
|
# Strikeout
|
||||||
nwGUI.docEditor.setCursorPosition(50)
|
nwGUI.docEditor.setCursorPosition(50)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.STRIKE) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.STRIKE) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "~~consectetur~~")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "~~consectetur~~")
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Redo
|
# Redo
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.REDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.REDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "~~consectetur~~")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "~~consectetur~~")
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Shortcodes
|
||||||
|
# ==========
|
||||||
|
|
||||||
|
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||||
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
|
# Italic
|
||||||
|
nwGUI.docEditor.setCursorPosition(46)
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.SC_ITALIC) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "[i]consectetur[/i]")
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Bold
|
||||||
|
nwGUI.docEditor.setCursorPosition(46)
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.SC_BOLD) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "[b]consectetur[/b]")
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Strikethrough
|
||||||
|
nwGUI.docEditor.setCursorPosition(46)
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.SC_STRIKE) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "[s]consectetur[/s]")
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Underline
|
||||||
|
nwGUI.docEditor.setCursorPosition(46)
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.SC_ULINE) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "[u]consectetur[/u]")
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Superscript
|
||||||
|
nwGUI.docEditor.setCursorPosition(46)
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.SC_SUP) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "[sup]consectetur[/sup]")
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Subscript
|
||||||
|
nwGUI.docEditor.setCursorPosition(46)
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.SC_SUB) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "[sub]consectetur[/sub]")
|
||||||
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Quotes
|
# Quotes
|
||||||
# ======
|
# ======
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % ipsumText[0]
|
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
# Add Single Quotes
|
# Add Single Quotes
|
||||||
nwGUI.docEditor.setCursorPosition(50)
|
nwGUI.docEditor.setCursorPosition(50)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.S_QUOTE) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.S_QUOTE) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "\u2018consectetur\u2019")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "\u2018consectetur\u2019")
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Add Double Quotes
|
# Add Double Quotes
|
||||||
nwGUI.docEditor.setCursorPosition(50)
|
nwGUI.docEditor.setCursorPosition(50)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.D_QUOTE) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.D_QUOTE) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "\u201cconsectetur\u201d")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "\u201cconsectetur\u201d")
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.UNDO) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Replace Single Quotes
|
# Replace Single Quotes
|
||||||
repText = theText.replace("consectetur", "'consectetur'")
|
repText = text.replace("consectetur", "'consectetur'")
|
||||||
nwGUI.docEditor.replaceText(repText)
|
nwGUI.docEditor.replaceText(repText)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) is True
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.REPL_SNG) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.REPL_SNG) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "\u2018consectetur\u2019")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "\u2018consectetur\u2019")
|
||||||
|
|
||||||
# Replace Double Quotes
|
# Replace Double Quotes
|
||||||
repText = theText.replace("consectetur", "\"consectetur\"")
|
repText = text.replace("consectetur", "\"consectetur\"")
|
||||||
nwGUI.docEditor.replaceText(repText)
|
nwGUI.docEditor.replaceText(repText)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) is True
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.REPL_DBL) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.REPL_DBL) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "\u201cconsectetur\u201d")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "\u201cconsectetur\u201d")
|
||||||
|
|
||||||
# Remove Line Breaks
|
# Remove Line Breaks
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % ipsumText[0]
|
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||||
repText = theText[:100] + theText[100:].replace(" ", "\n", 3)
|
repText = text[:100] + text[100:].replace(" ", "\n", 3)
|
||||||
nwGUI.docEditor.replaceText(repText)
|
nwGUI.docEditor.replaceText(repText)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.RM_BREAKS) is True
|
assert nwGUI.docEditor.docAction(nwDocAction.RM_BREAKS) is True
|
||||||
assert nwGUI.docEditor.getText().strip() == theText.strip()
|
assert nwGUI.docEditor.getText().strip() == text.strip()
|
||||||
|
|
||||||
# Format Block
|
# Format Block
|
||||||
# ============
|
# ============
|
||||||
|
|
||||||
theText = "## Scene Title\n\nScene text.\n\n"
|
text = "## Scene Title\n\nScene text.\n\n"
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
# Header 1
|
# Header 1
|
||||||
nwGUI.docEditor.setCursorPosition(0)
|
nwGUI.docEditor.setCursorPosition(0)
|
||||||
@@ -437,20 +484,125 @@ def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
|
|||||||
# END Test testGuiEditor_Actions
|
# END Test testGuiEditor_Actions
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testGuiEditor_ToolBar(qtbot, nwGUI, projPath, mockRnd):
|
||||||
|
"""Test the document actions. This is not an extensive test of the
|
||||||
|
action features, just that the actions are actually called. The
|
||||||
|
various action features are tested when their respective functions
|
||||||
|
are tested.
|
||||||
|
"""
|
||||||
|
buildTestProject(nwGUI, projPath)
|
||||||
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
|
|
||||||
|
docEditor: GuiDocEditor = nwGUI.docEditor
|
||||||
|
docToolBar: GuiDocToolBar = docEditor.docToolBar
|
||||||
|
|
||||||
|
text = (
|
||||||
|
"### A Scene\n\n"
|
||||||
|
"Text bold one\n\n"
|
||||||
|
"Text bold two\n\n"
|
||||||
|
"Text italic one\n\n"
|
||||||
|
"Text italic two\n\n"
|
||||||
|
"Text strikethrough one\n\n"
|
||||||
|
"Text strikethrough two\n\n"
|
||||||
|
"Text underline one\n\n"
|
||||||
|
"Text superscript one\n\n"
|
||||||
|
"Text subscript one\n\n"
|
||||||
|
)
|
||||||
|
length = len(text)
|
||||||
|
docEditor.replaceText(text)
|
||||||
|
assert len(docEditor.getText()) == length
|
||||||
|
|
||||||
|
# Show the ToolBar
|
||||||
|
assert docToolBar.isVisible() is False
|
||||||
|
docEditor._toggleToolBarVisibility()
|
||||||
|
assert docToolBar.isVisible() is True
|
||||||
|
|
||||||
|
# Markdown Mode
|
||||||
|
assert docToolBar.tbMode.isChecked() is False
|
||||||
|
|
||||||
|
# Click Bold
|
||||||
|
docEditor.setCursorPosition(20)
|
||||||
|
docToolBar.tbBold.click()
|
||||||
|
assert len(docEditor.getText()) == length + 4
|
||||||
|
|
||||||
|
# Click Italic
|
||||||
|
docEditor.setCursorPosition(54)
|
||||||
|
docToolBar.tbItalic.click()
|
||||||
|
assert len(docEditor.getText()) == length + 6
|
||||||
|
|
||||||
|
# Click Strikethrough
|
||||||
|
docEditor.setCursorPosition(90)
|
||||||
|
docToolBar.tbStrike.click()
|
||||||
|
assert len(docEditor.getText()) == length + 10
|
||||||
|
|
||||||
|
# Shortcode Mode
|
||||||
|
docToolBar.tbMode.click()
|
||||||
|
assert docToolBar.tbMode.isChecked() is True
|
||||||
|
|
||||||
|
# Click Bold
|
||||||
|
docEditor.setCursorPosition(39)
|
||||||
|
docToolBar.tbBold.click()
|
||||||
|
assert len(docEditor.getText()) == length + 17
|
||||||
|
|
||||||
|
# Click Italic
|
||||||
|
docEditor.setCursorPosition(80)
|
||||||
|
docToolBar.tbItalic.click()
|
||||||
|
assert len(docEditor.getText()) == length + 24
|
||||||
|
|
||||||
|
# Click Strikethrough
|
||||||
|
docEditor.setCursorPosition(132)
|
||||||
|
docToolBar.tbStrike.click()
|
||||||
|
assert len(docEditor.getText()) == length + 31
|
||||||
|
|
||||||
|
# Click Underline
|
||||||
|
docEditor.setCursorPosition(163)
|
||||||
|
docToolBar.tbUnderline.click()
|
||||||
|
assert len(docEditor.getText()) == length + 38
|
||||||
|
|
||||||
|
# Click Superscript
|
||||||
|
docEditor.setCursorPosition(190)
|
||||||
|
docToolBar.tbSuperscript.click()
|
||||||
|
assert len(docEditor.getText()) == length + 49
|
||||||
|
|
||||||
|
# Click Subscript
|
||||||
|
docEditor.setCursorPosition(223)
|
||||||
|
docToolBar.tbSubscript.click()
|
||||||
|
assert len(docEditor.getText()) == length + 60
|
||||||
|
|
||||||
|
# Check Result
|
||||||
|
assert docEditor.getText() == (
|
||||||
|
"### A Scene\n\n"
|
||||||
|
"Text **bold** one\n\n"
|
||||||
|
"Text [b]bold[/b] two\n\n"
|
||||||
|
"Text _italic_ one\n\n"
|
||||||
|
"Text [i]italic[/i] two\n\n"
|
||||||
|
"Text ~~strikethrough~~ one\n\n"
|
||||||
|
"Text [s]strikethrough[/s] two\n\n"
|
||||||
|
"Text [u]underline[/u] one\n\n"
|
||||||
|
"Text [sup]superscript[/sup] one\n\n"
|
||||||
|
"Text [sub]subscript[/sub] one\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
# qtbot.stop()
|
||||||
|
|
||||||
|
# END Test testGuiEditor_ToolBar
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd):
|
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd):
|
||||||
"""Test the document insert functions."""
|
"""Test the document insert functions."""
|
||||||
buildTestProject(nwGUI, projPath)
|
buildTestProject(nwGUI, projPath)
|
||||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
# Insert Text
|
# Insert Text
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % ipsumText[0]
|
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
# No Document Handle
|
# No Document Handle
|
||||||
nwGUI.docEditor._docHandle = None
|
nwGUI.docEditor._docHandle = None
|
||||||
@@ -461,23 +613,23 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd
|
|||||||
# Insert String
|
# Insert String
|
||||||
nwGUI.docEditor.setCursorPosition(24)
|
nwGUI.docEditor.setCursorPosition(24)
|
||||||
assert nwGUI.docEditor.insertText(", ipsumer,") is True
|
assert nwGUI.docEditor.insertText(", ipsumer,") is True
|
||||||
assert nwGUI.docEditor.getText() == theText[:24] + ", ipsumer," + theText[24:]
|
assert nwGUI.docEditor.getText() == text[:24] + ", ipsumer," + text[24:]
|
||||||
|
|
||||||
# Single Quotes
|
# Single Quotes
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(41)
|
nwGUI.docEditor.setCursorPosition(41)
|
||||||
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_LS) is True
|
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_LS) is True
|
||||||
nwGUI.docEditor.setCursorPosition(53)
|
nwGUI.docEditor.setCursorPosition(53)
|
||||||
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_RS) is True
|
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_RS) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "\u2018consectetur\u2019")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "\u2018consectetur\u2019")
|
||||||
|
|
||||||
# Double Quotes
|
# Double Quotes
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(41)
|
nwGUI.docEditor.setCursorPosition(41)
|
||||||
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_LD) is True
|
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_LD) is True
|
||||||
nwGUI.docEditor.setCursorPosition(53)
|
nwGUI.docEditor.setCursorPosition(53)
|
||||||
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_RD) is True
|
assert nwGUI.docEditor.insertText(nwDocInsert.QUOTE_RD) is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "\u201cconsectetur\u201d")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "\u201cconsectetur\u201d")
|
||||||
|
|
||||||
# Invalid Inserts
|
# Invalid Inserts
|
||||||
assert nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) is False
|
assert nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) is False
|
||||||
@@ -486,18 +638,18 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd
|
|||||||
# Insert KeyWords
|
# Insert KeyWords
|
||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
theText = "### A Scene\n\n\n%s" % ipsumText[0]
|
text = "### A Scene\n\n\n%s" % ipsumText[0]
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorLine(3)
|
nwGUI.docEditor.setCursorLine(3)
|
||||||
|
|
||||||
# Invalid Keyword
|
# Invalid Keyword
|
||||||
assert nwGUI.docEditor.insertKeyWord("stuff") is False
|
assert nwGUI.docEditor.insertKeyWord("stuff") is False
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Valid Keyword
|
# Valid Keyword
|
||||||
assert nwGUI.docEditor.insertKeyWord(nwKeyWords.POV_KEY) is True
|
assert nwGUI.docEditor.insertKeyWord(nwKeyWords.POV_KEY) is True
|
||||||
assert nwGUI.docEditor.insertText("Jane\n")
|
assert nwGUI.docEditor.insertText("Jane\n")
|
||||||
assert nwGUI.docEditor.getText() == theText.replace(
|
assert nwGUI.docEditor.getText() == text.replace(
|
||||||
"\n\n\n", "\n\n@pov: Jane\n\n", 1
|
"\n\n\n", "\n\n@pov: Jane\n\n", 1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -510,7 +662,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd
|
|||||||
nwGUI.docEditor.setCursorPosition(20)
|
nwGUI.docEditor.setCursorPosition(20)
|
||||||
assert nwGUI.docEditor.insertKeyWord(nwKeyWords.CHAR_KEY) is True
|
assert nwGUI.docEditor.insertKeyWord(nwKeyWords.CHAR_KEY) is True
|
||||||
assert nwGUI.docEditor.insertText("John")
|
assert nwGUI.docEditor.insertText("John")
|
||||||
assert nwGUI.docEditor.getText() == theText.replace(
|
assert nwGUI.docEditor.getText() == text.replace(
|
||||||
"\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
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -525,45 +677,45 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
buildTestProject(nwGUI, projPath)
|
buildTestProject(nwGUI, projPath)
|
||||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
|
||||||
# Clear Surrounding
|
# Clear Surrounding
|
||||||
# =================
|
# =================
|
||||||
|
|
||||||
# No Selection
|
# No Selection
|
||||||
theText = "### A Scene\n\n%s" % ipsumText[0]
|
text = "### A Scene\n\n%s" % ipsumText[0]
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
|
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
cursor = nwGUI.docEditor.textCursor()
|
||||||
assert nwGUI.docEditor._clearSurrounding(theCursor, 1) is False
|
assert nwGUI.docEditor._clearSurrounding(cursor, 1) is False
|
||||||
|
|
||||||
# Clear Characters, 1 Layer
|
# Clear Characters, 1 Layer
|
||||||
repText = theText.replace("consectetur", "=consectetur=")
|
repText = text.replace("consectetur", "=consectetur=")
|
||||||
nwGUI.docEditor.replaceText(repText)
|
nwGUI.docEditor.replaceText(repText)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
|
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
cursor = nwGUI.docEditor.textCursor()
|
||||||
theCursor.select(QTextCursor.WordUnderCursor)
|
cursor.select(QTextCursor.WordUnderCursor)
|
||||||
assert nwGUI.docEditor._clearSurrounding(theCursor, 1) is True
|
assert nwGUI.docEditor._clearSurrounding(cursor, 1) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Clear Characters, 2 Layers
|
# Clear Characters, 2 Layers
|
||||||
repText = theText.replace("consectetur", "==consectetur==")
|
repText = text.replace("consectetur", "==consectetur==")
|
||||||
nwGUI.docEditor.replaceText(repText)
|
nwGUI.docEditor.replaceText(repText)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
|
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
cursor = nwGUI.docEditor.textCursor()
|
||||||
theCursor.select(QTextCursor.WordUnderCursor)
|
cursor.select(QTextCursor.WordUnderCursor)
|
||||||
assert nwGUI.docEditor._clearSurrounding(theCursor, 2) is True
|
assert nwGUI.docEditor._clearSurrounding(cursor, 2) is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Wrap Selection
|
# Wrap Selection
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2])
|
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2])
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
|
|
||||||
# No Selection
|
# No Selection
|
||||||
@@ -572,23 +724,23 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
assert nwGUI.docEditor._wrapSelection("=", "=") is False
|
assert nwGUI.docEditor._wrapSelection("=", "=") is False
|
||||||
|
|
||||||
# Wrap Equal
|
# Wrap Equal
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._wrapSelection("=") is True
|
assert nwGUI.docEditor._wrapSelection("=") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "=consectetur=")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur=")
|
||||||
|
|
||||||
# Wrap Unequal
|
# Wrap Unequal
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._wrapSelection("=", "*") is True
|
assert nwGUI.docEditor._wrapSelection("=", "*") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "=consectetur*")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur*")
|
||||||
|
|
||||||
# Past Paragraph
|
# Past Paragraph
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
cursor = nwGUI.docEditor.textCursor()
|
||||||
theCursor.setPosition(13, QTextCursor.MoveAnchor)
|
cursor.setPosition(13, QTextCursor.MoveAnchor)
|
||||||
theCursor.setPosition(1000, QTextCursor.KeepAnchor)
|
cursor.setPosition(1000, QTextCursor.KeepAnchor)
|
||||||
nwGUI.docEditor.setTextCursor(theCursor)
|
nwGUI.docEditor.setTextCursor(cursor)
|
||||||
assert nwGUI.docEditor._wrapSelection("=") is True
|
assert nwGUI.docEditor._wrapSelection("=") is True
|
||||||
|
|
||||||
newText = nwGUI.docEditor.getText()
|
newText = nwGUI.docEditor.getText()
|
||||||
@@ -599,8 +751,8 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
# Toggle Format
|
# Toggle Format
|
||||||
# =============
|
# =============
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2])
|
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2])
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
|
|
||||||
# No Selection
|
# No Selection
|
||||||
@@ -609,17 +761,17 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
assert nwGUI.docEditor._toggleFormat(2, "=") is False
|
assert nwGUI.docEditor._toggleFormat(2, "=") is False
|
||||||
|
|
||||||
# Wrap Single Equal
|
# Wrap Single Equal
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "=consectetur=")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur=")
|
||||||
|
|
||||||
# Past Paragraph
|
# Past Paragraph
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
cursor = nwGUI.docEditor.textCursor()
|
||||||
theCursor.setPosition(13, QTextCursor.MoveAnchor)
|
cursor.setPosition(13, QTextCursor.MoveAnchor)
|
||||||
theCursor.setPosition(1000, QTextCursor.KeepAnchor)
|
cursor.setPosition(1000, QTextCursor.KeepAnchor)
|
||||||
nwGUI.docEditor.setTextCursor(theCursor)
|
nwGUI.docEditor.setTextCursor(cursor)
|
||||||
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
||||||
|
|
||||||
newText = nwGUI.docEditor.getText()
|
newText = nwGUI.docEditor.getText()
|
||||||
@@ -628,31 +780,31 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
assert newPara[2] == ipsumText[1]
|
assert newPara[2] == ipsumText[1]
|
||||||
|
|
||||||
# Wrap Double Equal
|
# Wrap Double Equal
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "==consectetur==")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "==consectetur==")
|
||||||
|
|
||||||
# Toggle Double Equal
|
# Toggle Double Equal
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
||||||
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
||||||
assert nwGUI.docEditor.getText() == theText
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
# Toggle Triple+Double Equal
|
# Toggle Triple+Double Equal
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._toggleFormat(3, "=") is True
|
assert nwGUI.docEditor._toggleFormat(3, "=") is True
|
||||||
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
assert nwGUI.docEditor._toggleFormat(2, "=") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "=consectetur=")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "=consectetur=")
|
||||||
|
|
||||||
# Toggle Unequal
|
# Toggle Unequal
|
||||||
repText = theText.replace("consectetur", "=consectetur==")
|
repText = text.replace("consectetur", "=consectetur==")
|
||||||
nwGUI.docEditor.replaceText(repText)
|
nwGUI.docEditor.replaceText(repText)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("consectetur", "consectetur=")
|
assert nwGUI.docEditor.getText() == text.replace("consectetur", "consectetur=")
|
||||||
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
assert nwGUI.docEditor._toggleFormat(1, "=") is True
|
||||||
assert nwGUI.docEditor.getText() == repText
|
assert nwGUI.docEditor.getText() == repText
|
||||||
|
|
||||||
@@ -660,15 +812,15 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
# No Selection
|
# No Selection
|
||||||
theText = "### A Scene\n\n%s" % ipsumText[0].replace("consectetur", "=consectetur=")
|
text = "### A Scene\n\n%s" % ipsumText[0].replace("consectetur", "=consectetur=")
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is False
|
assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is False
|
||||||
|
|
||||||
# First Paragraph Selected
|
# First Paragraph Selected
|
||||||
# This should not replace anything in second paragraph
|
# This should not replace anything in second paragraph
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2]).replace("ipsum", "=ipsum=")
|
text = "### A Scene\n\n%s" % "\n\n".join(ipsumText[0:2]).replace("ipsum", "=ipsum=")
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA)
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_PARA)
|
||||||
assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is True
|
assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is True
|
||||||
@@ -679,12 +831,12 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
assert newPara[2] == ipsumText[1].replace("ipsum", "=ipsum=")
|
assert newPara[2] == ipsumText[1].replace("ipsum", "=ipsum=")
|
||||||
|
|
||||||
# Edge of Document
|
# Edge of Document
|
||||||
theText = ipsumText[0].replace("Lorem", "=Lorem=")
|
text = ipsumText[0].replace("Lorem", "=Lorem=")
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
|
assert nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
|
||||||
assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is True
|
assert nwGUI.docEditor._replaceQuotes("=", "<", ">") is True
|
||||||
assert nwGUI.docEditor.getText() == theText.replace("=Lorem=", "<Lorem>")
|
assert nwGUI.docEditor.getText() == text.replace("=Lorem=", "<Lorem>")
|
||||||
|
|
||||||
# Remove Line Breaks
|
# Remove Line Breaks
|
||||||
# ==================
|
# ==================
|
||||||
@@ -693,20 +845,20 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
parTwo = ipsumText[1].replace(" ", "\n", 5)
|
parTwo = ipsumText[1].replace(" ", "\n", 5)
|
||||||
|
|
||||||
# Remove All
|
# Remove All
|
||||||
theText = "### A Scene\n\n%s\n\n%s" % (parOne, parTwo)
|
text = "### A Scene\n\n%s\n\n%s" % (parOne, parTwo)
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
nwGUI.docEditor.setCursorPosition(45)
|
nwGUI.docEditor.setCursorPosition(45)
|
||||||
nwGUI.docEditor._removeInParLineBreaks()
|
nwGUI.docEditor._removeInParLineBreaks()
|
||||||
assert nwGUI.docEditor.getText() == "### A Scene\n\n%s\n" % "\n\n".join(ipsumText[0:2])
|
assert nwGUI.docEditor.getText() == "### A Scene\n\n%s\n" % "\n\n".join(ipsumText[0:2])
|
||||||
|
|
||||||
# Remove First Paragraph
|
# Remove First Paragraph
|
||||||
# Second paragraphs should remain unchanged
|
# Second paragraphs should remain unchanged
|
||||||
theText = "### A Scene\n\n%s\n\n%s" % (parOne, parTwo)
|
text = "### A Scene\n\n%s\n\n%s" % (parOne, parTwo)
|
||||||
nwGUI.docEditor.replaceText(theText)
|
nwGUI.docEditor.replaceText(text)
|
||||||
theCursor = nwGUI.docEditor.textCursor()
|
cursor = nwGUI.docEditor.textCursor()
|
||||||
theCursor.setPosition(16, QTextCursor.MoveAnchor)
|
cursor.setPosition(16, QTextCursor.MoveAnchor)
|
||||||
theCursor.setPosition(680, QTextCursor.KeepAnchor)
|
cursor.setPosition(680, QTextCursor.KeepAnchor)
|
||||||
nwGUI.docEditor.setTextCursor(theCursor)
|
nwGUI.docEditor.setTextCursor(cursor)
|
||||||
nwGUI.docEditor._removeInParLineBreaks()
|
nwGUI.docEditor._removeInParLineBreaks()
|
||||||
|
|
||||||
newText = nwGUI.docEditor.getText()
|
newText = nwGUI.docEditor.getText()
|
||||||
@@ -720,6 +872,25 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, projPath, ipsumTex
|
|||||||
assert newPara[6] == twoBits[4]
|
assert newPara[6] == twoBits[4]
|
||||||
assert newPara[7] == " ".join(twoBits[5:])
|
assert newPara[7] == " ".join(twoBits[5:])
|
||||||
|
|
||||||
|
# Key Press Events
|
||||||
|
# ================
|
||||||
|
text = "### A Scene\n\n%s\n\n%s" % (parOne, parTwo)
|
||||||
|
nwGUI.docEditor.replaceText(text)
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Select All
|
||||||
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_A, modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Delete, delay=KEY_DELAY)
|
||||||
|
assert nwGUI.docEditor.getText() == ""
|
||||||
|
|
||||||
|
# Undo
|
||||||
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Z, modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
|
assert nwGUI.docEditor.getText() == text
|
||||||
|
|
||||||
|
# Redo
|
||||||
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Y, modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||||
|
assert nwGUI.docEditor.getText() == ""
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_TextManipulation
|
# END Test testGuiEditor_TextManipulation
|
||||||
|
|||||||
@@ -594,14 +594,18 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd):
|
|||||||
# ==========
|
# ==========
|
||||||
|
|
||||||
# No document open, so not allowing focus mode
|
# No document open, so not allowing focus mode
|
||||||
assert nwGUI.toggleFocusMode() is False
|
nwGUI.toggleFocusMode()
|
||||||
|
assert nwGUI.treePane.isVisible() is True
|
||||||
|
assert nwGUI.mainStatus.isVisible() is True
|
||||||
|
assert nwGUI.mainMenu.isVisible() is True
|
||||||
|
assert nwGUI.sideBar.isVisible() is True
|
||||||
|
|
||||||
# Open a file in editor and viewer
|
# Open a file in editor and viewer
|
||||||
assert nwGUI.openDocument(C.hSceneDoc)
|
assert nwGUI.openDocument(C.hSceneDoc)
|
||||||
assert nwGUI.viewDocument(C.hSceneDoc)
|
assert nwGUI.viewDocument(C.hSceneDoc)
|
||||||
|
|
||||||
# Enable focus mode
|
# Enable focus mode
|
||||||
assert nwGUI.toggleFocusMode() is True
|
nwGUI.toggleFocusMode()
|
||||||
assert nwGUI.treePane.isVisible() is False
|
assert nwGUI.treePane.isVisible() is False
|
||||||
assert nwGUI.mainStatus.isVisible() is False
|
assert nwGUI.mainStatus.isVisible() is False
|
||||||
assert nwGUI.mainMenu.isVisible() is False
|
assert nwGUI.mainMenu.isVisible() is False
|
||||||
@@ -609,7 +613,7 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd):
|
|||||||
assert nwGUI.splitView.isVisible() is False
|
assert nwGUI.splitView.isVisible() is False
|
||||||
|
|
||||||
# Disable focus mode
|
# Disable focus mode
|
||||||
assert nwGUI.toggleFocusMode() is True
|
nwGUI.toggleFocusMode()
|
||||||
assert nwGUI.treePane.isVisible() is True
|
assert nwGUI.treePane.isVisible() is True
|
||||||
assert nwGUI.mainStatus.isVisible() is True
|
assert nwGUI.mainStatus.isVisible() is True
|
||||||
assert nwGUI.mainMenu.isVisible() is True
|
assert nwGUI.mainMenu.isVisible() is True
|
||||||
|
|||||||
@@ -322,17 +322,17 @@ def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, tstPaths):
|
|||||||
assert qPix.isNull() is True
|
assert qPix.isNull() is True
|
||||||
|
|
||||||
# Test image sizes
|
# Test image sizes
|
||||||
qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=None)
|
qPix = iconCache.loadDecoration("wiz-back", w=100, h=None)
|
||||||
assert qPix.isNull() is False
|
assert qPix.isNull() is False
|
||||||
assert qPix.width() == 100
|
assert qPix.width() == 100
|
||||||
assert qPix.height() > 100
|
assert qPix.height() > 100
|
||||||
|
|
||||||
qPix = iconCache.loadDecoration("wiz-back", pxW=None, pxH=100)
|
qPix = iconCache.loadDecoration("wiz-back", w=None, h=100)
|
||||||
assert qPix.isNull() is False
|
assert qPix.isNull() is False
|
||||||
assert qPix.width() < 100
|
assert qPix.width() < 100
|
||||||
assert qPix.height() == 100
|
assert qPix.height() == 100
|
||||||
|
|
||||||
qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=100)
|
qPix = iconCache.loadDecoration("wiz-back", w=100, h=100)
|
||||||
assert qPix.isNull() is False
|
assert qPix.isNull() is False
|
||||||
assert qPix.width() == 100
|
assert qPix.width() == 100
|
||||||
assert qPix.height() == 100
|
assert qPix.height() == 100
|
||||||
|
|||||||