diff --git a/nw/__init__.py b/nw/__init__.py index 3330c6b6..332b7009 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -31,6 +31,7 @@ import logging from os import path, remove, rename +from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication from nw.guimain import GuiMain @@ -225,6 +226,8 @@ def main(sysArgs=None): nwApp = QApplication([__package__,("-style=%s" % qtStyle)]) nwApp.setApplicationName(__package__) nwApp.setApplicationVersion(__version__) + nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) + nwApp.setOrganizationDomain("novelwriter.io") nwGUI = GuiMain() sys.exit(nwApp.exec_()) diff --git a/nw/assets/graphics/license.txt b/nw/assets/graphics/license.txt index 8a822fd2..00a23f87 100644 --- a/nw/assets/graphics/license.txt +++ b/nw/assets/graphics/license.txt @@ -2,7 +2,7 @@ FROM ICON SET: Typicons LICENSE: Creative Commons (Attribution-Share Alike 3.0 Unported) https://creativecommons.org/licenses/by-sa/3.0/ -Apllies to: -export.svg -merge.svg -split.svg +Applies to: + * graphics/export.svg + * graphics/merge.svg + * graphics/split.svg diff --git a/nw/assets/icons/fallback/status_lang-dark.svg b/nw/assets/icons/fallback/status_lang-dark.svg new file mode 100644 index 00000000..8966c4c9 --- /dev/null +++ b/nw/assets/icons/fallback/status_lang-dark.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/fallback/status_lang.svg b/nw/assets/icons/fallback/status_lang.svg new file mode 100644 index 00000000..255ac776 --- /dev/null +++ b/nw/assets/icons/fallback/status_lang.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/fallback/status_time-dark.svg b/nw/assets/icons/fallback/status_time-dark.svg new file mode 100644 index 00000000..7d489bae --- /dev/null +++ b/nw/assets/icons/fallback/status_time-dark.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/fallback/status_time.svg b/nw/assets/icons/fallback/status_time.svg new file mode 100644 index 00000000..129d58cf --- /dev/null +++ b/nw/assets/icons/fallback/status_time.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/globe-dark.svg b/nw/assets/icons/globe-dark.svg deleted file mode 100644 index bde909f3..00000000 --- a/nw/assets/icons/globe-dark.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/nw/assets/icons/globe.svg b/nw/assets/icons/globe.svg deleted file mode 100644 index ef97e998..00000000 --- a/nw/assets/icons/globe.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/nw/assets/icons/novelWriter.png b/nw/assets/icons/novelWriter.png new file mode 100644 index 00000000..4543f575 Binary files /dev/null and b/nw/assets/icons/novelWriter.png differ diff --git a/nw/assets/icons/typicons_colour_dark/README.md b/nw/assets/icons/typicons_colour_dark/README.md new file mode 100644 index 00000000..cfa5ea08 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/README.md @@ -0,0 +1,5 @@ +## Typicons + +Copyright: Stephen Hutchings +Source: https://github.com/stephenhutchings/typicons.font +License: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) diff --git a/nw/assets/icons/typicons_colour_dark/backspace.svg b/nw/assets/icons/typicons_colour_dark/backspace.svg new file mode 100644 index 00000000..153e6519 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/backspace.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/book.svg b/nw/assets/icons/typicons_colour_dark/book.svg new file mode 100644 index 00000000..efd40186 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/book.svg @@ -0,0 +1,67 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/cancel.svg b/nw/assets/icons/typicons_colour_dark/cancel.svg new file mode 100644 index 00000000..e915d902 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/cancel.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/delete.svg b/nw/assets/icons/typicons_colour_dark/delete.svg new file mode 100644 index 00000000..1a5f0eac --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/delete.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/document-text.svg b/nw/assets/icons/typicons_colour_dark/document-text.svg new file mode 100644 index 00000000..aadcc635 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/document-text.svg @@ -0,0 +1,63 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/download.svg b/nw/assets/icons/typicons_colour_dark/download.svg new file mode 100644 index 00000000..c8ffa937 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/download.svg @@ -0,0 +1,63 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/flag.svg b/nw/assets/icons/typicons_colour_dark/flag.svg new file mode 100644 index 00000000..9facfcfa --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/flag.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/folder.svg b/nw/assets/icons/typicons_colour_dark/folder.svg new file mode 100644 index 00000000..fd894938 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/folder.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/globe.svg b/nw/assets/icons/typicons_colour_dark/globe.svg new file mode 100644 index 00000000..9394ebed --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/globe.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/globe_max.svg b/nw/assets/icons/typicons_colour_dark/globe_max.svg new file mode 100644 index 00000000..8966c4c9 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/globe_max.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/icons.conf b/nw/assets/icons/typicons_colour_dark/icons.conf new file mode 100644 index 00000000..dfb1278f --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/icons.conf @@ -0,0 +1,41 @@ +## +# Icons: Typicons Colour Dark +# Source: https://github.com/stephenhutchings/typicons.font +# Credit: Stephen Hutchings +# Modified: Veronica Berglyd Olsen +## + +[Main] +name = Typicons Colour Dark +description = Coulorised icons for dark GUI theme based on Typicons. +author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings +url = https://github.com/stephenhutchings/typicons.font +license = CC BY-SA 4.0 +licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ + +[Map] +cls_none = cancel.svg +cls_novel = book.svg +cls_plot = puzzle.svg +cls_character = user.svg +cls_world = location.svg +cls_timeline = time.svg +cls_object = key.svg +cls_entity = flag.svg +cls_custom = star.svg +cls_trash = trash.svg +proj_document = document-text.svg +proj_folder = folder.svg +status_lang = globe_max.svg +status_time = stopwatch_max.svg +delete = delete.svg +add = plus.svg +remove = minus.svg +close = times.svg +done = input-checked.svg +search = search.svg +search-replace = search-replace.svg +clear = backspace.svg +save = download.svg +edit = pencil.svg diff --git a/nw/assets/icons/typicons_colour_dark/input-checked.svg b/nw/assets/icons/typicons_colour_dark/input-checked.svg new file mode 100644 index 00000000..a4b66a4d --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/input-checked.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/key.svg b/nw/assets/icons/typicons_colour_dark/key.svg new file mode 100644 index 00000000..ea2d07de --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/key.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/location.svg b/nw/assets/icons/typicons_colour_dark/location.svg new file mode 100644 index 00000000..1189343d --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/location.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/minus.svg b/nw/assets/icons/typicons_colour_dark/minus.svg new file mode 100644 index 00000000..c2f9ccee --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/minus.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/pencil.svg b/nw/assets/icons/typicons_colour_dark/pencil.svg new file mode 100644 index 00000000..873f98e4 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/pencil.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/plus.svg b/nw/assets/icons/typicons_colour_dark/plus.svg new file mode 100644 index 00000000..8e77765e --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/plus.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/puzzle.svg b/nw/assets/icons/typicons_colour_dark/puzzle.svg new file mode 100644 index 00000000..45f00f47 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/puzzle.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/search-replace.svg b/nw/assets/icons/typicons_colour_dark/search-replace.svg new file mode 100644 index 00000000..012a9795 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/search-replace.svg @@ -0,0 +1,72 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/search.svg b/nw/assets/icons/typicons_colour_dark/search.svg new file mode 100644 index 00000000..40a6203c --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/search.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/star.svg b/nw/assets/icons/typicons_colour_dark/star.svg new file mode 100644 index 00000000..93995a6c --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/star.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/time-dark.svg b/nw/assets/icons/typicons_colour_dark/stopwatch.svg similarity index 76% rename from nw/assets/icons/time-dark.svg rename to nw/assets/icons/typicons_colour_dark/stopwatch.svg index bdb740c1..7fbbf0d7 100644 --- a/nw/assets/icons/time-dark.svg +++ b/nw/assets/icons/typicons_colour_dark/stopwatch.svg @@ -8,27 +8,28 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.2" - width="21" - height="21" - viewBox="0 0 21 21" - id="svg1039" - sodipodi:docname="time-dark.svg" - inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> + width="24" + height="24" + viewBox="0 0 24 24" + id="svg7030" + sodipodi:docname="stopwatch.svg" + inkscape:version="1.0rc1 (09960d6f05, 2020-04-09)"> + id="metadata7036"> image/svg+xml - + + id="defs7034" /> + inkscape:current-layer="svg7030" /> diff --git a/nw/assets/icons/typicons_colour_dark/stopwatch_max.svg b/nw/assets/icons/typicons_colour_dark/stopwatch_max.svg new file mode 100644 index 00000000..7d489bae --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/stopwatch_max.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/time.svg b/nw/assets/icons/typicons_colour_dark/time.svg new file mode 100644 index 00000000..ac0587cb --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/time.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/times.svg b/nw/assets/icons/typicons_colour_dark/times.svg new file mode 100644 index 00000000..c360709a --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/times.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/trash.svg b/nw/assets/icons/typicons_colour_dark/trash.svg new file mode 100644 index 00000000..853a6743 --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/trash.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_colour_dark/user.svg b/nw/assets/icons/typicons_colour_dark/user.svg new file mode 100644 index 00000000..b27888cc --- /dev/null +++ b/nw/assets/icons/typicons_colour_dark/user.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/README.md b/nw/assets/icons/typicons_grey_dark/README.md new file mode 100644 index 00000000..cfa5ea08 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/README.md @@ -0,0 +1,5 @@ +## Typicons + +Copyright: Stephen Hutchings +Source: https://github.com/stephenhutchings/typicons.font +License: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) diff --git a/nw/assets/icons/typicons_grey_dark/backspace.svg b/nw/assets/icons/typicons_grey_dark/backspace.svg new file mode 100644 index 00000000..7fd677bc --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/backspace.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/book.svg b/nw/assets/icons/typicons_grey_dark/book.svg new file mode 100644 index 00000000..3b576f27 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/book.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/cancel.svg b/nw/assets/icons/typicons_grey_dark/cancel.svg new file mode 100644 index 00000000..6d718a07 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/cancel.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/delete.svg b/nw/assets/icons/typicons_grey_dark/delete.svg new file mode 100644 index 00000000..560666dd --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/delete.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/document-text.svg b/nw/assets/icons/typicons_grey_dark/document-text.svg new file mode 100644 index 00000000..e67fb843 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/document-text.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/download.svg b/nw/assets/icons/typicons_grey_dark/download.svg new file mode 100644 index 00000000..7dfffc01 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/download.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/flag.svg b/nw/assets/icons/typicons_grey_dark/flag.svg new file mode 100644 index 00000000..475d7415 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/flag.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/folder.svg b/nw/assets/icons/typicons_grey_dark/folder.svg new file mode 100644 index 00000000..1050ce57 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/folder.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/globe.svg b/nw/assets/icons/typicons_grey_dark/globe.svg new file mode 100644 index 00000000..9394ebed --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/globe.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/globe_max.svg b/nw/assets/icons/typicons_grey_dark/globe_max.svg new file mode 100644 index 00000000..8966c4c9 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/globe_max.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/icons.conf b/nw/assets/icons/typicons_grey_dark/icons.conf new file mode 100644 index 00000000..70cef96c --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/icons.conf @@ -0,0 +1,40 @@ +## +# Icons: Typicons Grey Dark +# Source: https://github.com/stephenhutchings/typicons.font +# Credit: Stephen Hutchings +## + +[Main] +name = Typicons Grey Dark +description = Greyscaled icons for dark GUI theme based on Typicons. +author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings +url = https://github.com/stephenhutchings/typicons.font +license = CC BY-SA 4.0 +licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ + +[Map] +cls_none = cancel.svg +cls_novel = book.svg +cls_plot = puzzle.svg +cls_character = user.svg +cls_world = location.svg +cls_timeline = time.svg +cls_object = key.svg +cls_entity = flag.svg +cls_custom = star.svg +cls_trash = trash.svg +proj_document = document-text.svg +proj_folder = folder.svg +status_lang = globe_max.svg +status_time = stopwatch_max.svg +delete = delete.svg +add = plus.svg +remove = minus.svg +close = times.svg +done = input-checked.svg +search = search.svg +search-replace = search-replace.svg +clear = backspace.svg +save = download.svg +edit = pencil.svg diff --git a/nw/assets/icons/typicons_grey_dark/input-checked.svg b/nw/assets/icons/typicons_grey_dark/input-checked.svg new file mode 100644 index 00000000..0bf72e27 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/input-checked.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/key.svg b/nw/assets/icons/typicons_grey_dark/key.svg new file mode 100644 index 00000000..37d91a7a --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/key.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/location.svg b/nw/assets/icons/typicons_grey_dark/location.svg new file mode 100644 index 00000000..d429d0dd --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/location.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/minus.svg b/nw/assets/icons/typicons_grey_dark/minus.svg new file mode 100644 index 00000000..0cd0201c --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/minus.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/pencil.svg b/nw/assets/icons/typicons_grey_dark/pencil.svg new file mode 100644 index 00000000..bf1d0dab --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/pencil.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/plus.svg b/nw/assets/icons/typicons_grey_dark/plus.svg new file mode 100644 index 00000000..8b7f4a6d --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/plus.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/puzzle.svg b/nw/assets/icons/typicons_grey_dark/puzzle.svg new file mode 100644 index 00000000..719497cb --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/puzzle.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/search-replace.svg b/nw/assets/icons/typicons_grey_dark/search-replace.svg new file mode 100644 index 00000000..048de135 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/search-replace.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/search.svg b/nw/assets/icons/typicons_grey_dark/search.svg new file mode 100644 index 00000000..5ea743b8 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/search.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/star.svg b/nw/assets/icons/typicons_grey_dark/star.svg new file mode 100644 index 00000000..a750ffd5 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/star.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/time.svg b/nw/assets/icons/typicons_grey_dark/stopwatch.svg similarity index 72% rename from nw/assets/icons/time.svg rename to nw/assets/icons/typicons_grey_dark/stopwatch.svg index 920d9bd7..7fbbf0d7 100644 --- a/nw/assets/icons/time.svg +++ b/nw/assets/icons/typicons_grey_dark/stopwatch.svg @@ -8,27 +8,28 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.2" - width="21" - height="21" - viewBox="0 0 21 21" - id="svg1039" - sodipodi:docname="time.svg" - inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> + width="24" + height="24" + viewBox="0 0 24 24" + id="svg7030" + sodipodi:docname="stopwatch.svg" + inkscape:version="1.0rc1 (09960d6f05, 2020-04-09)"> + id="metadata7036"> image/svg+xml - + + id="defs7034" /> + inkscape:current-layer="svg7030" /> + id="g7028" + transform="matrix(0.95238095,0,0,0.95238095,0.5714286,0.09523812)" + style="fill:#aeaeae;fill-opacity:1"> + style="fill:#aeaeae;fill-opacity:1" /> diff --git a/nw/assets/icons/typicons_grey_dark/stopwatch_max.svg b/nw/assets/icons/typicons_grey_dark/stopwatch_max.svg new file mode 100644 index 00000000..7d489bae --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/stopwatch_max.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/time.svg b/nw/assets/icons/typicons_grey_dark/time.svg new file mode 100644 index 00000000..12f47768 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/time.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/times.svg b/nw/assets/icons/typicons_grey_dark/times.svg new file mode 100644 index 00000000..64410dfb --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/times.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/trash.svg b/nw/assets/icons/typicons_grey_dark/trash.svg new file mode 100644 index 00000000..b7728f3e --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/trash.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_dark/user.svg b/nw/assets/icons/typicons_grey_dark/user.svg new file mode 100644 index 00000000..3b7cdf02 --- /dev/null +++ b/nw/assets/icons/typicons_grey_dark/user.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/README.md b/nw/assets/icons/typicons_grey_light/README.md new file mode 100644 index 00000000..cfa5ea08 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/README.md @@ -0,0 +1,5 @@ +## Typicons + +Copyright: Stephen Hutchings +Source: https://github.com/stephenhutchings/typicons.font +License: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) diff --git a/nw/assets/icons/typicons_grey_light/backspace.svg b/nw/assets/icons/typicons_grey_light/backspace.svg new file mode 100644 index 00000000..16008fc7 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/backspace.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/book.svg b/nw/assets/icons/typicons_grey_light/book.svg new file mode 100644 index 00000000..10e53ab5 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/book.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/cancel.svg b/nw/assets/icons/typicons_grey_light/cancel.svg new file mode 100644 index 00000000..43b5ced6 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/cancel.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/delete.svg b/nw/assets/icons/typicons_grey_light/delete.svg new file mode 100644 index 00000000..2983650a --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/delete.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/document-text.svg b/nw/assets/icons/typicons_grey_light/document-text.svg new file mode 100644 index 00000000..f4cbef49 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/document-text.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/download.svg b/nw/assets/icons/typicons_grey_light/download.svg new file mode 100644 index 00000000..fa67f171 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/download.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/flag.svg b/nw/assets/icons/typicons_grey_light/flag.svg new file mode 100644 index 00000000..75d87698 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/flag.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/folder.svg b/nw/assets/icons/typicons_grey_light/folder.svg new file mode 100644 index 00000000..180e26de --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/folder.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/globe.svg b/nw/assets/icons/typicons_grey_light/globe.svg new file mode 100644 index 00000000..83c97666 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/globe.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/globe_max.svg b/nw/assets/icons/typicons_grey_light/globe_max.svg new file mode 100644 index 00000000..255ac776 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/globe_max.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/icons.conf b/nw/assets/icons/typicons_grey_light/icons.conf new file mode 100644 index 00000000..fc4d556e --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/icons.conf @@ -0,0 +1,40 @@ +## +# Icons: Typicons Grey Light +# Source: https://github.com/stephenhutchings/typicons.font +# Credit: Stephen Hutchings +## + +[Main] +name = Typicons Grey Light +description = Greyscaled icons for light GUI theme based on Typicons. +author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings +url = https://github.com/stephenhutchings/typicons.font +license = CC BY-SA 4.0 +licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ + +[Map] +cls_none = cancel.svg +cls_novel = book.svg +cls_plot = puzzle.svg +cls_character = user.svg +cls_world = location.svg +cls_timeline = time.svg +cls_object = key.svg +cls_entity = flag.svg +cls_custom = star.svg +cls_trash = trash.svg +proj_document = document-text.svg +proj_folder = folder.svg +status_lang = globe_max.svg +status_time = stopwatch_max.svg +delete = delete.svg +add = plus.svg +remove = minus.svg +close = times.svg +done = input-checked.svg +search = search.svg +search-replace = search-replace.svg +clear = backspace.svg +save = download.svg +edit = pencil.svg diff --git a/nw/assets/icons/typicons_grey_light/input-checked.svg b/nw/assets/icons/typicons_grey_light/input-checked.svg new file mode 100644 index 00000000..ba650c23 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/input-checked.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/key.svg b/nw/assets/icons/typicons_grey_light/key.svg new file mode 100644 index 00000000..0a0f2a38 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/key.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/location.svg b/nw/assets/icons/typicons_grey_light/location.svg new file mode 100644 index 00000000..e5c361f7 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/location.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/minus.svg b/nw/assets/icons/typicons_grey_light/minus.svg new file mode 100644 index 00000000..77d38693 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/minus.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/pencil.svg b/nw/assets/icons/typicons_grey_light/pencil.svg new file mode 100644 index 00000000..1e51a280 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/pencil.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/plus.svg b/nw/assets/icons/typicons_grey_light/plus.svg new file mode 100644 index 00000000..56efaa2a --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/plus.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/puzzle.svg b/nw/assets/icons/typicons_grey_light/puzzle.svg new file mode 100644 index 00000000..29034bd3 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/puzzle.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/search-replace.svg b/nw/assets/icons/typicons_grey_light/search-replace.svg new file mode 100644 index 00000000..90597a8d --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/search-replace.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/search.svg b/nw/assets/icons/typicons_grey_light/search.svg new file mode 100644 index 00000000..6ffb3f23 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/search.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/star.svg b/nw/assets/icons/typicons_grey_light/star.svg new file mode 100644 index 00000000..1371ea80 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/star.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/stopwatch.svg b/nw/assets/icons/typicons_grey_light/stopwatch.svg new file mode 100644 index 00000000..df7327c0 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/stopwatch.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/stopwatch_max.svg b/nw/assets/icons/typicons_grey_light/stopwatch_max.svg new file mode 100644 index 00000000..129d58cf --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/stopwatch_max.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/time.svg b/nw/assets/icons/typicons_grey_light/time.svg new file mode 100644 index 00000000..9e47aa48 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/time.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/times.svg b/nw/assets/icons/typicons_grey_light/times.svg new file mode 100644 index 00000000..ff91c26a --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/times.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/trash.svg b/nw/assets/icons/typicons_grey_light/trash.svg new file mode 100644 index 00000000..98746b48 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/trash.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/assets/icons/typicons_grey_light/user.svg b/nw/assets/icons/typicons_grey_light/user.svg new file mode 100644 index 00000000..d950dd25 --- /dev/null +++ b/nw/assets/icons/typicons_grey_light/user.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/nw/config.py b/nw/config.py index c0c34215..81e87961 100644 --- a/nw/config.py +++ b/nw/config.py @@ -83,6 +83,7 @@ class Config: ## General self.guiTheme = "default" self.guiSyntax = "default_light" + self.guiIcons = "default" self.guiDark = False self.guiLang = "en" # Hardcoded for now @@ -304,6 +305,9 @@ class Config: self.guiSyntax = self._parseLine( cnfParse, cnfSec, "syntax", self.CNF_STR, self.guiSyntax ) + self.guiIcons = self._parseLine( + cnfParse, cnfSec, "icons", self.CNF_STR, self.guiIcons + ) self.guiDark = self._parseLine( cnfParse, cnfSec, "guidark", self.CNF_BOOL, self.guiDark ) @@ -452,6 +456,7 @@ class Config: cnfParse.set(cnfSec,"timestamp", formatTimeStamp(time())) cnfParse.set(cnfSec,"theme", str(self.guiTheme)) cnfParse.set(cnfSec,"syntax", str(self.guiSyntax)) + cnfParse.set(cnfSec,"icons", str(self.guiIcons)) cnfParse.set(cnfSec,"guidark", str(self.guiDark)) ## Sizes diff --git a/nw/constants/constants.py b/nw/constants/constants.py index 3b61fccc..96a97bc1 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -87,6 +87,18 @@ class nwLabels(): nwItemClass.CUSTOM : "X", nwItemClass.TRASH : "R", } + CLASS_ICON = { + nwItemClass.NO_CLASS : "cls_none", + nwItemClass.NOVEL : "cls_novel", + nwItemClass.PLOT : "cls_plot", + nwItemClass.CHARACTER : "cls_character", + nwItemClass.WORLD : "cls_world", + nwItemClass.TIMELINE : "cls_timeline", + nwItemClass.OBJECT : "cls_object", + nwItemClass.ENTITY : "cls_entity", + nwItemClass.CUSTOM : "cls_custom", + nwItemClass.TRASH : "cls_trash", + } LAYOUT_NAME = { nwItemLayout.NO_LAYOUT : "None", nwItemLayout.TITLE : "Title Page", diff --git a/nw/gui/dialogs/configeditor.py b/nw/gui/dialogs/configeditor.py index e3bf4f7b..e03d7bad 100644 --- a/nw/gui/dialogs/configeditor.py +++ b/nw/gui/dialogs/configeditor.py @@ -58,8 +58,11 @@ class GuiConfigEditor(QDialog): self.innerBox = QVBoxLayout() self.setWindowTitle("Preferences") - self.guiDeco = self.theParent.theTheme.loadDecoration("settings",(64,64)) + self.guiDeco = self.theParent.theTheme.loadDecoration("settings", (64,64)) + self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) + self.outerBox.addLayout(self.innerBox) self.outerBox.setSpacing(16) + self.setLayout(self.outerBox) self.tabGeneral = GuiConfigEditGeneralTab(self.theParent) self.tabLayout = GuiConfigEditLayoutTab(self.theParent) @@ -73,10 +76,6 @@ class GuiConfigEditor(QDialog): self.tabWidget.addTab(self.tabEditing, "Editing") self.tabWidget.addTab(self.tabAutoRep, "Auto-Replace") - self.setLayout(self.outerBox) - self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) - self.outerBox.addLayout(self.innerBox) - self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self._doSave) self.buttonBox.rejected.connect(self._doClose) @@ -170,6 +169,22 @@ class GuiConfigEditGeneralTab(QWidget): "Changing this requires restarting %s." % nw.__package__ ) + ## Select Icon Theme + self.selectIcons = QComboBox() + self.selectIcons.setMinimumWidth(200) + self.theIcons = self.theTheme.theIcons.listThemes() + for iconDir, iconName in self.theIcons: + self.selectIcons.addItem(iconName, iconDir) + iconIdx = self.selectIcons.findData(self.mainConf.guiIcons) + if iconIdx != -1: + self.selectIcons.setCurrentIndex(iconIdx) + + self.mainForm.addRow( + "Main icon theme", + self.selectIcons, + "Changing this requires restarting %s." % nw.__package__ + ) + ## Syntax Highlighting self.selectSyntax = QComboBox() self.selectSyntax.setMinimumWidth(200) @@ -182,7 +197,8 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Syntax highlight theme", - self.selectSyntax + self.selectSyntax, + "" ) ## Dark Icons @@ -239,7 +255,7 @@ class GuiConfigEditGeneralTab(QWidget): ## Backup Path self.backupPath = self.mainConf.backupPath - self.backupGetPath = QPushButton(self.theTheme.getIcon("folder"),"Select Folder") + self.backupGetPath = QPushButton(self.theTheme.getIcon("folder-open"),"Select Folder") self.backupGetPath.clicked.connect(self._backupFolder) self.backupPathRow = self.mainForm.addRow( "Backup storage location", @@ -276,6 +292,7 @@ class GuiConfigEditGeneralTab(QWidget): guiTheme = self.selectTheme.currentData() guiSyntax = self.selectSyntax.currentData() + guiIcons = self.selectIcons.currentData() guiDark = self.preferDarkIcons.isChecked() showFullPath = self.showFullPath.isChecked() autoSaveDoc = self.autoSaveDoc.value() @@ -286,9 +303,11 @@ class GuiConfigEditGeneralTab(QWidget): # Check if restart is needed needsRestart |= self.mainConf.guiTheme != guiTheme + needsRestart |= self.mainConf.guiIcons != guiIcons self.mainConf.guiTheme = guiTheme self.mainConf.guiSyntax = guiSyntax + self.mainConf.guiIcons = guiIcons self.mainConf.guiDark = guiDark self.mainConf.showFullPath = showFullPath self.mainConf.autoSaveDoc = autoSaveDoc diff --git a/nw/gui/dialogs/itemeditor.py b/nw/gui/dialogs/itemeditor.py index c94e053e..d757b9fc 100644 --- a/nw/gui/dialogs/itemeditor.py +++ b/nw/gui/dialogs/itemeditor.py @@ -57,12 +57,16 @@ class GuiItemEditor(QDialog): self._doClose() self.setWindowTitle("Item Settings") - self.guiDeco = self.theParent.theTheme.loadDecoration("settings",(64,64)) - self.outerBox.setSpacing(16) - - self.setLayout(self.outerBox) + self.guiDeco = QLabel() + self.guiDeco.setPixmap( + self.theParent.theTheme.getPixmap( + nwLabels.CLASS_ICON[self.theItem.itemClass], (64,64) + ) + ) self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) self.outerBox.addLayout(self.innerBox) + self.outerBox.setSpacing(16) + self.setLayout(self.outerBox) self.mainGroup = QGroupBox("Item Settings") self.mainForm = QGridLayout() diff --git a/nw/gui/dialogs/projecteditor.py b/nw/gui/dialogs/projecteditor.py index 3df14269..fd256c3a 100644 --- a/nw/gui/dialogs/projecteditor.py +++ b/nw/gui/dialogs/projecteditor.py @@ -52,12 +52,18 @@ class GuiProjectEditor(QDialog): self.theParent = theParent self.theProject = theProject - self.outerBox = QHBoxLayout() - self.innerBox = QVBoxLayout() + self.outerBox = QHBoxLayout() + self.innerBox = QVBoxLayout() self.setWindowTitle("Project Settings") - self.guiDeco = self.theParent.theTheme.loadDecoration("settings",(64,64)) + self.guiDeco = QLabel() + self.guiDeco.setPixmap( + self.theParent.theTheme.getPixmap("cls_novel", (64,64)) + ) + self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) + self.outerBox.addLayout(self.innerBox) self.outerBox.setSpacing(16) + self.setLayout(self.outerBox) self.theProject.countStatus() self.tabMain = GuiProjectEditMain(self.theParent, self.theProject) @@ -71,14 +77,10 @@ class GuiProjectEditor(QDialog): self.tabWidget.addTab(self.tabImport, "Importance") self.tabWidget.addTab(self.tabReplace,"Auto-Replace") - self.outerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) - self.outerBox.addLayout(self.innerBox) - self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self._doSave) self.buttonBox.rejected.connect(self._doClose) - self.setLayout(self.outerBox) self.innerBox.addWidget(self.tabWidget) self.innerBox.addWidget(self.buttonBox) @@ -361,9 +363,12 @@ class GuiProjectEditReplace(QWidget): self.editKey = QLineEdit() self.editValue = QLineEdit() - self.saveButton = QPushButton(self.theTheme.getIcon("save"),"") + self.saveButton = QPushButton(self.theTheme.getIcon("done"),"") self.addButton = QPushButton(self.theTheme.getIcon("add"),"") self.delButton = QPushButton(self.theTheme.getIcon("remove"),"") + self.saveButton.setToolTip("Save entry") + self.addButton.setToolTip("Add new entry") + self.delButton.setToolTip("Delete selected entry") self.editKey.setEnabled(False) self.editKey.setMaxLength(40) diff --git a/nw/gui/elements/doctree.py b/nw/gui/elements/doctree.py index 4b0a3ed2..ebff504f 100644 --- a/nw/gui/elements/doctree.py +++ b/nw/gui/elements/doctree.py @@ -31,7 +31,7 @@ import nw from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QFont, QColor from PyQt5.QtWidgets import ( - QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication, QMessageBox + qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox ) from nw.core import NWDoc @@ -207,7 +207,7 @@ class GuiDocTree(QTreeWidget): """Move an item up or down in the tree, but only if the treeView has focus. This also applies when the menu is used. """ - if QApplication.focusWidget() == self and self.theParent.hasProject: + if qApp.focusWidget() == self and self.theParent.hasProject: tHandle = self.getSelectedHandle() tItem = self._getTreeItem(tHandle) pItem = tItem.parent() @@ -524,6 +524,7 @@ class GuiDocTree(QTreeWidget): tHandle = nwItem.itemHandle pHandle = nwItem.parHandle + tClass = nwItem.itemClass newItem = QTreeWidgetItem([""]*4) newItem.setText(self.C_NAME, "") @@ -553,13 +554,13 @@ class GuiDocTree(QTreeWidget): newItem.setExpanded(nwItem.isExpanded) if nwItem.itemType == nwItemType.ROOT: - newItem.setIcon(self.C_NAME, self.theTheme.getIcon("root")) + newItem.setIcon(self.C_NAME, self.theTheme.getIcon(nwLabels.CLASS_ICON[tClass])) elif nwItem.itemType == nwItemType.FOLDER: - newItem.setIcon(self.C_NAME, self.theTheme.getIcon("folder")) + newItem.setIcon(self.C_NAME, self.theTheme.getIcon("proj_folder")) elif nwItem.itemType == nwItemType.FILE: - newItem.setIcon(self.C_NAME, self.theTheme.getIcon("document")) + newItem.setIcon(self.C_NAME, self.theTheme.getIcon("proj_document")) elif nwItem.itemType == nwItemType.TRASH: - newItem.setIcon(self.C_NAME, self.theTheme.getIcon("trash")) + newItem.setIcon(self.C_NAME, self.theTheme.getIcon(nwLabels.CLASS_ICON[tClass])) return newItem @@ -588,7 +589,7 @@ class GuiDocTree(QTreeWidget): self.addTopLevelItem(newItem) self.orphRoot = newItem newItem.setExpanded(True) - newItem.setIcon(self.C_NAME, self.theTheme.getIcon("orphan")) + newItem.setIcon(self.C_NAME, self.theTheme.getIcon("warning")) return def _cleanOrphanedRoot(self): diff --git a/nw/gui/elements/searchbar.py b/nw/gui/elements/searchbar.py index 68518d8d..431bddad 100644 --- a/nw/gui/elements/searchbar.py +++ b/nw/gui/elements/searchbar.py @@ -30,7 +30,7 @@ import nw from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( - QFrame, QGridLayout, QLabel, QLineEdit, QPushButton, QApplication + qApp, QFrame, QGridLayout, QLabel, QLineEdit, QPushButton ) from nw.constants import nwDocAction @@ -60,7 +60,7 @@ class GuiSearchBar(QFrame): self.replaceLabel = QLabel("Replace") self.closeButton = QPushButton(self.theTheme.getIcon("close"),"") self.searchButton = QPushButton(self.theTheme.getIcon("search"),"") - self.replaceButton = QPushButton(self.theTheme.getIcon("replace"),"") + self.replaceButton = QPushButton(self.theTheme.getIcon("search-replace"),"") self.closeButton.clicked.connect(self._doClose) self.searchButton.clicked.connect(self._doSearch) @@ -127,7 +127,7 @@ class GuiSearchBar(QFrame): return def _doSearch(self): - modKey = QApplication.keyboardModifiers() + modKey = qApp.keyboardModifiers() if modKey == Qt.ShiftModifier: self.theParent.docEditor.docAction(nwDocAction.GO_PREV) else: diff --git a/nw/gui/icons.py b/nw/gui/icons.py index d74ae685..35bc8122 100644 --- a/nw/gui/icons.py +++ b/nw/gui/icons.py @@ -26,13 +26,15 @@ """ import logging +import configparser import nw -from os import path +from os import path, listdir from PyQt5.QtCore import QSize from PyQt5.QtSvg import QSvgWidget from PyQt5.QtGui import QIcon, QPixmap +from PyQt5.QtWidgets import QStyle, qApp logger = logging.getLogger(__name__) @@ -44,19 +46,34 @@ class GuiIcons: # https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html # or, if there is no fallback, the variable should be None. ICON_MAP = { - "root" : "drive-harddisk", - "folder" : "folder", - "document" : "x-office-document", - "trash" : "user-trash", - "orphan" : "dialog-warning", - "save" : "document-save", - "add" : "list-add", - "remove" : "list-remove", - "close" : "edit-delete", - "search" : "edit-find", - "replace" : "edit-find-replace", - "time" : None, - "globe" : None, + "cls_none" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_novel" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_plot" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_character" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_world" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_timeline" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_object" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_entity" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_custom" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "cls_trash" : (QStyle.SP_DriveHDIcon, "drive-harddisk"), + "proj_document" : (QStyle.SP_FileIcon, "x-office-document"), + "proj_folder" : (QStyle.SP_DirIcon, "folder"), + "status_lang" : (None, None), + "status_time" : (None, None), + ## Button Icons + "folder-open" : (QStyle.SP_DirOpenIcon, "folder-open"), + "delete" : (QStyle.SP_DialogDiscardButton, "edit-delete"), + "add" : (None, "list-add"), + "remove" : (None, "list-remove"), + "close" : (QStyle.SP_DialogCloseButton, "window-close"), + "done" : (QStyle.SP_DialogApplyButton, None), + "search" : (None, "edit-find"), + "search-replace" : (None, "edit-find-replace"), + "clear" : (QStyle.SP_LineEditClearButton, "clear_left"), + "save" : (QStyle.SP_DialogSaveButton, "document-save"), + "edit" : (None, None), + ## Other Icons + "warning" : (QStyle.SP_MessageBoxWarning, "dialog-warning"), } DECO_MAP = { @@ -73,34 +90,89 @@ class GuiIcons: self.theParent = theParent # Storage - self.qIcons = {} + self.qIcons = {} + self.themeMap = {} + self.themeList = [] + self.fbackName = "fallback" + self.confName = "icons.conf" - # Look for files in - self.priPath = "" # The gui theme's icon folder - self.secPath = "" # The main assets icon folder - self.prefDark = False # Load dark icons, if available + # Icon Theme Path + self.iconPath = None + self.confFile = None + + # Icon Theme Meta + self.themeName = "" + self.themeDescription = "" + self.themeAuthor = "" + self.themeCredit = "" + self.themeUrl = "" + self.themeLicense = "" + self.themeLicenseUrl = "" return - def initIcons(self, priPath): - """Load all icons listed in the icon map. Can be overridden by - the selected theme. - """ + ## + # Actions + ## - self.priPath = priPath - self.secPath = self.mainConf.iconPath - self.prefDark = self.mainConf.guiDark + def updateTheme(self): - for iconKey in self.ICON_MAP.keys(): - self.qIcons[iconKey] = self._loadIcon(iconKey) + logger.debug("Loading icon theme files") - return + self.themeMap = {} + checkPath = path.join(self.mainConf.iconPath, self.mainConf.guiIcons) + if path.isdir(checkPath): + logger.debug("Loading icon theme '%s'" % self.mainConf.guiIcons) + self.iconPath = checkPath + self.confFile = path.join(checkPath, self.confName) + else: + return False + + # Config File + confParser = configparser.ConfigParser() + try: + confParser.read_file(open(self.confFile, mode="r", encoding="utf8")) + except Exception as e: + logger.error("Could not load icon theme settings from: %s" % self.confFile) + return False + + ## Main + cnfSec = "Main" + if confParser.has_section(cnfSec): + self.themeName = self._parseLine( confParser, cnfSec, "name", "") + self.themeDescription = self._parseLine( confParser, cnfSec, "description", "") + self.themeAuthor = self._parseLine( confParser, cnfSec, "author", "") + self.themeCredit = self._parseLine( confParser, cnfSec, "credit", "") + self.themeUrl = self._parseLine( confParser, cnfSec, "url", "") + self.themeLicense = self._parseLine( confParser, cnfSec, "license", "") + self.themeLicenseUrl = self._parseLine( confParser, cnfSec, "licenseurl", "") + + ## Palette + cnfSec = "Map" + if confParser.has_section(cnfSec): + for iconName, iconFile in confParser.items(cnfSec): + if iconName not in self.ICON_MAP: + logger.error("Unknown icon name '%s' in config file" % iconName) + else: + iconPath = path.join(self.iconPath, iconFile) + if path.isfile(iconPath): + self.themeMap[iconName] = iconPath + logger.verbose("Icon slot '%s' using file '%s'" % (iconName, iconFile)) + else: + logger.error("Icon file '%s' not in theme folder" % iconFile) + + logger.info("Loaded icon theme '%s'" % self.mainConf.guiIcons) + + return True + + ## + # Access Functions + ## def loadDecoration(self, decoKey, decoSize=None): """Load graphical decoration element based on the decoration map. This function always returns a QSwgWidget. """ - if decoKey not in self.DECO_MAP: logger.error("Decoration with name '%s' does not exist" % decoKey) return QSvgWidget() @@ -122,19 +194,53 @@ class GuiIcons: def getIcon(self, iconKey, iconSize=None): """Return an icon from the icon buffer. If it doesn't exist, - return an empty icon. + return, load it, and if it still doesn't exist, return an empty + icon. """ if iconKey in self.qIcons: return self.qIcons[iconKey] - return QIcon() + else: + qIcon = self._loadIcon(iconKey) + self.qIcons[iconKey] = qIcon + return qIcon def getPixmap(self, iconKey, iconSize): """Return an icon from the icon buffer as a QPixmap. If it doesn't exist, return an empty QPixmap. """ - if iconKey in self.qIcons: - return self.qIcons[iconKey].pixmap(iconSize[0], iconSize[1], QIcon.Normal) - return QPixmap() + qIcon = self.getIcon(iconKey) + return qIcon.pixmap(iconSize[0], iconSize[1], QIcon.Normal) + + def listThemes(self): + """Scan the icons themes folder and list all themes. + """ + if self.themeList: + return self.themeList + + confParser = configparser.ConfigParser() + for themeDir in listdir(self.mainConf.iconPath): + themePath = path.join(self.mainConf.iconPath, themeDir) + if not path.isdir(themePath) or themeDir == self.fbackName: + continue + themeConf = path.join(themePath, self.confName) + logger.verbose("Checking icon theme config for '%s'" % themeDir) + try: + confParser.read_file(open(themeConf, mode="r", encoding="utf8")) + except Exception as e: + self.theParent.makeAlert(["Could not load theme config file.",str(e)],nwAlert.ERROR) + continue + themeName = "" + if confParser.has_section("Main"): + if confParser.has_option("Main", "name"): + themeName = confParser.get("Main", "name") + logger.verbose("Theme name is '%s'" % themeName) + if themeName != "": + self.themeList.append((themeDir, themeName)) + + self.themeList = sorted(self.themeList, key=lambda x: x[1]) + self.themeList.insert(0, ("default", "System Icons")) + + return self.themeList ## # Internal Functions @@ -148,49 +254,41 @@ class GuiIcons: """ if iconKey not in self.ICON_MAP: - logger.error("Icon with name '%s' does not exist" % iconKey) + logger.error("Requested unknown icon name '%s'" % iconKey) return QIcon() - if self.prefDark: - iconSuffix = "dark" - else: - iconSuffix = "light" + if iconKey in self.themeMap: + logger.verbose("Loading: %s" % path.relpath(self.themeMap[iconKey])) + return QIcon(self.themeMap[iconKey]) - iconNames = [] - iconNames.append("%s-%s.svg" % (iconKey, iconSuffix)) - iconNames.append("%s-%s.png" % (iconKey, iconSuffix)) - iconNames.append("%s.svg" % iconKey) - iconNames.append("%s.png" % iconKey) - - # Check theme folder - loadFrom = None - for iconName in iconNames: - checkPath = path.join(self.priPath, iconName) - if path.isfile(checkPath): - loadFrom = checkPath - logger.verbose("Loading icon '%s' from theme" % iconName) - break - if loadFrom is not None: - return QIcon(loadFrom) - - # Check assets folder - for iconName in iconNames: - checkPath = path.join(self.secPath, iconName) - if path.isfile(checkPath): - loadFrom = checkPath - logger.verbose("Loading icon '%s' from assets" % iconName) - break - if loadFrom is not None: - return QIcon(loadFrom) + # Next, we try to load the Qt style icons + if self.ICON_MAP[iconKey][0] is not None: + logger.verbose("Loading icon '%s' from Qt QStyle.standardIcon" % iconKey) + return qApp.style().standardIcon(self.ICON_MAP[iconKey][0]) # If we're still here, try to set from system theme - if self.ICON_MAP[iconKey] is not None: + if self.ICON_MAP[iconKey][1] is not None: logger.verbose("Loading icon '%s' from system theme" % iconKey) - return QIcon().fromTheme(self.ICON_MAP[iconKey]) + return QIcon().fromTheme(self.ICON_MAP[iconKey][1]) - # Give up and return an empty icomn + # Finally. we check if we have a fallback icon + if self.mainConf.guiDark: + fbackIcon = path.join(self.mainConf.iconPath, self.fbackName, "%s-dark.svg" % iconKey) + else: + fbackIcon = path.join(self.mainConf.iconPath, self.fbackName, "%s.svg" % iconKey) + if path.isfile(fbackIcon): + logger.verbose("Loading icon '%s' from fallback theme" % iconKey) + return QIcon(fbackIcon) + + # Give up and return an empty icon logger.warning("Did not load an icon for '%s'" % iconKey) return QIcon() + def _parseLine(self, confParser, cnfSec, cnfName, cnfDefault): + if confParser.has_section(cnfSec): + if confParser.has_option(cnfSec, cnfName): + return confParser.get(cnfSec, cnfName) + return cnfDefault + # END Class GuiIcons diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 56ec0337..8126766b 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -148,6 +148,7 @@ class GuiMainMenu(QMenuBar): credits = "
".join(["%s%s" % (listPrefix, x) for x in nw.__credits__]), ) theTheme = self.theParent.theTheme + theIcons = self.theParent.theTheme.theIcons if theTheme.themeCredit != "" or theTheme.syntaxCredit != "": aboutMsg += "

GUI Theme and Syntax Highlighting

" aboutMsg += "

" @@ -155,6 +156,10 @@ class GuiMainMenu(QMenuBar): aboutMsg += "%s\"%s\" by %s
" % ( listPrefix, theTheme.themeName, theTheme.themeCredit ) + if theIcons.themeCredit != "": + aboutMsg += "%s\"%s\" by %s
" % ( + listPrefix, theIcons.themeName, theIcons.themeCredit + ) if theTheme.syntaxCredit != "": aboutMsg += "%s\"%s\" by %s
" % ( listPrefix, theTheme.syntaxName, theTheme.syntaxCredit diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index d815dd54..50f218f5 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -68,7 +68,7 @@ class GuiMainStatus(QStatusBar): self.timeBox.setFont(self.monoFont) self.timeIcon = QLabel() - self.timeIcon.setPixmap(self.theParent.theTheme.getPixmap("time",(14,14))) + self.timeIcon.setPixmap(self.theParent.theTheme.getPixmap("status_time",(14,14))) self.boxCounts = QLabel() self.boxCounts.setToolTip("Document Character | Word | Paragraph Count") @@ -85,7 +85,7 @@ class GuiMainStatus(QStatusBar): self.langBox = QLabel("None") self.langIcon = QLabel("") - self.langIcon.setPixmap(self.theParent.theTheme.getPixmap("globe",(14,14))) + self.langIcon.setPixmap(self.theParent.theTheme.getPixmap("status_lang",(14,14))) # Add Them self.addPermanentWidget(self.langIcon) diff --git a/nw/gui/theme.py b/nw/gui/theme.py index 0e4e5eaf..31e862e0 100644 --- a/nw/gui/theme.py +++ b/nw/gui/theme.py @@ -106,6 +106,7 @@ class GuiTheme: self.cssFile = None self.updateTheme() + self.theIcons.updateTheme() self.getIcon = self.theIcons.getIcon self.getPixmap = self.theIcons.getPixmap @@ -133,8 +134,6 @@ class GuiTheme: self.confFile = path.join(self.themePath,self.confName) self.cssFile = path.join(self.themePath,self.cssName) - self.theIcons.initIcons(path.join(self.themePath,self.iconPath)) - self.loadTheme() self.loadSyntax() @@ -261,8 +260,9 @@ class GuiTheme: return True def listThemes(self): - - if len(self.themeList) > 0: + """Scan the gui themes folder and list all themes. + """ + if self.themeList: return self.themeList confParser = configparser.ConfigParser() @@ -276,8 +276,9 @@ class GuiTheme: continue themeName = "" if confParser.has_section("Main"): - themeName = confParser.get("Main","name") - logger.verbose("Theme name is '%s'" % themeName) + if confParser.has_option("Main", "name"): + themeName = confParser.get("Main", "name") + logger.verbose("Theme name is '%s'" % themeName) if themeName != "": self.themeList.append((themeDir, themeName)) @@ -286,8 +287,9 @@ class GuiTheme: return self.themeList def listSyntax(self): - - if len(self.syntaxList) > 0: + """Scan the syntax themes folder and list all themes. + """ + if self.syntaxList: return self.syntaxList confParser = configparser.ConfigParser() @@ -304,7 +306,8 @@ class GuiTheme: return [] syntaxName = "" if confParser.has_section("Main"): - syntaxName = confParser.get("Main","name") + if confParser.has_option("Main", "name"): + syntaxName = confParser.get("Main", "name") if len(syntaxFile) > 5 and syntaxName != "": self.syntaxList.append((syntaxFile[:-5], syntaxName)) logger.verbose("Syntax name is '%s'" % syntaxName) diff --git a/nw/guimain.py b/nw/guimain.py index 8a37386c..c3c00fd6 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -58,11 +58,6 @@ class GuiMain(QMainWindow): logger.info("Starting %s" % nw.__package__) logger.debug("Initialising GUI ...") self.mainConf = nw.CONFIG - self.theTheme = GuiTheme(self) - self.theProject = NWProject(self) - self.theIndex = NWIndex(self.theProject, self) - self.hasProject = False - self.isZenMode = False # Some runtime info useful for debugging logger.info("OS: %s" % self.mainConf.osType) @@ -78,6 +73,13 @@ class GuiMain(QMainWindow): self.mainConf.verPyString, self.mainConf.verPyHexVal) ) + # Core Classes and settings + self.theTheme = GuiTheme(self) + self.theProject = NWProject(self) + self.theIndex = NWIndex(self.theProject, self) + self.hasProject = False + self.isZenMode = False + # Prepare main window self.resize(*self.mainConf.winGeometry) self._setWindowTitle() diff --git a/tests/reference/novelwriter.conf b/tests/reference/novelwriter.conf index 7102783d..13c3637a 100644 --- a/tests/reference/novelwriter.conf +++ b/tests/reference/novelwriter.conf @@ -2,6 +2,7 @@ timestamp = 2020-05-01 21:28:25 theme = default syntax = default_light +icons = default guidark = False [Sizes]