From 583e10adecbc815e7960fbd9f261e36a91b20dde Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 15 Jun 2019 15:24:29 +0200 Subject: [PATCH] Added dark tomorrow theme and separated gui theme from syntax --- nw/config.py | 7 +- nw/gui/configeditor.py | 17 ++- nw/gui/doceditor.py | 14 +- nw/gui/dochighlight.py | 40 ++++-- nw/gui/doctree.py | 2 +- nw/gui/statusbar.py | 12 +- nw/gui/winmain.py | 3 +- nw/theme.py | 128 +++++++++++++----- nw/themes/default_light/styles.css | 7 - nw/themes/default_light/theme.conf | 18 --- nw/themes/gui/default/styles.css | 17 +++ nw/themes/gui/default/theme.conf | 3 + nw/themes/syntax/base16_tomorrow_night.conf | 44 ++++++ .../theme.conf => syntax/default_dark.conf} | 0 nw/themes/syntax/default_light.conf | 18 +++ 15 files changed, 239 insertions(+), 91 deletions(-) delete mode 100644 nw/themes/default_light/styles.css delete mode 100644 nw/themes/default_light/theme.conf create mode 100644 nw/themes/gui/default/styles.css create mode 100644 nw/themes/gui/default/theme.conf create mode 100644 nw/themes/syntax/base16_tomorrow_night.conf rename nw/themes/{default_dark/theme.conf => syntax/default_dark.conf} (100%) create mode 100644 nw/themes/syntax/default_light.conf diff --git a/nw/config.py b/nw/config.py index bb8498b1..cb90cd48 100644 --- a/nw/config.py +++ b/nw/config.py @@ -51,7 +51,8 @@ class Config: self.confChanged = False ## General - self.guiTheme = "default_dark" + self.guiTheme = "default" + self.guiSyntax = "default_light" self.winGeometry = [1100, 650] self.treeColWidth = [120, 30, 50] self.mainPanePos = [300, 800] @@ -144,7 +145,8 @@ class Config: ## Main cnfSec = "Main" - self.guiTheme = self._parseLine(cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme) + self.guiTheme = self._parseLine(cnfParse, cnfSec, "theme", self.CNF_STR, self.guiTheme) + self.guiSyntax = self._parseLine(cnfParse, cnfSec, "syntax", self.CNF_STR, self.guiSyntax) ## Sizes cnfSec = "Sizes" @@ -197,6 +199,7 @@ class Config: cnfParse.add_section(cnfSec) cnfParse.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) cnfParse.set(cnfSec,"theme", str(self.guiTheme)) + cnfParse.set(cnfSec,"syntax", str(self.guiSyntax)) ## Sizes cnfSec = "Sizes" diff --git a/nw/gui/configeditor.py b/nw/gui/configeditor.py index 569a6402..7b79ee6a 100644 --- a/nw/gui/configeditor.py +++ b/nw/gui/configeditor.py @@ -129,8 +129,19 @@ class GuiConfigEditGeneral(QWidget): if themeIdx != -1: self.guiLookTheme.setCurrentIndex(themeIdx) - self.guiLookForm.addWidget(QLabel("Theme"), 0, 0) - self.guiLookForm.addWidget(self.guiLookTheme, 0, 1) + self.guiLookSyntax = QComboBox() + self.guiLookSyntax.setMinimumWidth(200) + self.theSyntaxes = self.theParent.theTheme.listSyntax() + for syntaxFile, syntaxName in self.theSyntaxes: + self.guiLookSyntax.addItem(syntaxName, syntaxFile) + syntaxIdx = self.guiLookSyntax.findData(self.mainConf.guiSyntax) + if syntaxIdx != -1: + self.guiLookSyntax.setCurrentIndex(syntaxIdx) + + self.guiLookForm.addWidget(QLabel("Theme"), 0, 0) + self.guiLookForm.addWidget(self.guiLookTheme, 0, 1) + self.guiLookForm.addWidget(QLabel("Syntax"), 1, 0) + self.guiLookForm.addWidget(self.guiLookSyntax, 1, 1) # Spell Checking self.spellLang = QGroupBox("Spell Checker", self) @@ -188,6 +199,7 @@ class GuiConfigEditGeneral(QWidget): needsRestart = False guiTheme = self.guiLookTheme.currentData() + guiSyntax = self.guiLookSyntax.currentData() spellLanguage = self.spellLangList.currentData() autoSaveDoc = self.autoSaveDoc.value() autoSaveProj = self.autoSaveProj.value() @@ -196,6 +208,7 @@ class GuiConfigEditGeneral(QWidget): needsRestart |= self.mainConf.guiTheme != guiTheme self.mainConf.guiTheme = guiTheme + self.mainConf.guiSyntax = guiSyntax self.mainConf.spellLanguage = spellLanguage self.mainConf.autoSaveDoc = autoSaveDoc self.mainConf.autoSaveProj = autoSaveProj diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index a4b7d04a..befc6c9d 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -134,7 +134,6 @@ class GuiDocEditor(QTextEdit): # Set text fixed width, or alternatively, just margins self.qDocument.setDocumentMargin(self.mainConf.textMargin) - self.changeWidth() # Also set the document text options for the document text flow theOpt = QTextOption() @@ -147,10 +146,13 @@ class GuiDocEditor(QTextEdit): # If we have a document open, we should reload it in case the font changed if self.theHandle is not None: tHandle = self.theHandle - self.hLight.initHighlighter() self.clearEditor() self.loadText(tHandle) + self.hLight.initHighlighter() + self.hLight.rehighlight() + self.changeWidth() + return True def loadText(self, tHandle): @@ -220,18 +222,14 @@ class GuiDocEditor(QTextEdit): def setSpellCheck(self, theMode): self.spellCheck = theMode self.hLight.setSpellCheck(theMode) - self.rehighlightDocument() + self.hLight.rehighlight() return True def updateSpellCheck(self): if self.spellCheck: - self.rehighlightDocument() + self.hLight.rehighlight() return True - def rehighlightDocument(self): - self.hLight.rehighlight() - return - ## # General Class Methods ## diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py index 97fb1e89..f4c8e1f2 100644 --- a/nw/gui/dochighlight.py +++ b/nw/gui/dochighlight.py @@ -36,18 +36,18 @@ class GuiDocHighlighter(QSyntaxHighlighter): self.hRules = [] self.hStyles = {} - self.colHead = QColor(*self.theTheme.colHead) - self.colHeadH = QColor(*self.theTheme.colHeadH) - self.colEmph = QColor(*self.theTheme.colEmph) - self.colDialN = QColor(*self.theTheme.colDialN) - self.colDialD = QColor(*self.theTheme.colDialD) - self.colDialS = QColor(*self.theTheme.colDialS) - self.colComm = QColor(*self.theTheme.colComm) - self.colKey = QColor(*self.theTheme.colKey) - self.colVal = QColor(*self.theTheme.colVal) - self.colSpell = QColor(*self.theTheme.colSpell) - self.colTagErr = QColor(*self.theTheme.colTagErr) - self.colRepTag = QColor(*self.theTheme.colRepTag) + self.colHead = QColor(0,0,0) + self.colHeadH = QColor(0,0,0) + self.colEmph = QColor(0,0,0) + self.colDialN = QColor(0,0,0) + self.colDialD = QColor(0,0,0) + self.colDialS = QColor(0,0,0) + self.colComm = QColor(0,0,0) + self.colKey = QColor(0,0,0) + self.colVal = QColor(0,0,0) + self.colSpell = QColor(0,0,0) + self.colTagErr = QColor(0,0,0) + self.colRepTag = QColor(0,0,0) self.initHighlighter() @@ -57,6 +57,21 @@ class GuiDocHighlighter(QSyntaxHighlighter): def initHighlighter(self): + logger.debug("Setting up highlighting rules") + + self.colHead = QColor(*self.theTheme.colHead) + self.colHeadH = QColor(*self.theTheme.colHeadH) + self.colEmph = QColor(*self.theTheme.colEmph) + self.colDialN = QColor(*self.theTheme.colDialN) + self.colDialD = QColor(*self.theTheme.colDialD) + self.colDialS = QColor(*self.theTheme.colDialS) + self.colComm = QColor(*self.theTheme.colComm) + self.colKey = QColor(*self.theTheme.colKey) + self.colVal = QColor(*self.theTheme.colVal) + self.colSpell = QColor(*self.theTheme.colSpell) + self.colTagErr = QColor(*self.theTheme.colTagErr) + self.colRepTag = QColor(*self.theTheme.colRepTag) + self.hStyles = { "header1" : self._makeFormat(self.colHead, "bold",1.8), "header2" : self._makeFormat(self.colHead, "bold",1.6), @@ -80,6 +95,7 @@ class GuiDocHighlighter(QSyntaxHighlighter): } # Headers + self.hRules = [] self.hRules.append(( r"^(#{1}) (.*)[^\n]", { 0 : self.hStyles["header1"], diff --git a/nw/gui/doctree.py b/nw/gui/doctree.py index 3614306e..a3f8f307 100644 --- a/nw/gui/doctree.py +++ b/nw/gui/doctree.py @@ -360,7 +360,7 @@ class GuiDocTree(QTreeWidget): newItem.setText(self.C_FLAGS, "") newItem.setText(self.C_HANDLE, tHandle) - newItem.setForeground(self.C_COUNT,QColor(0,105,135)) + newItem.setForeground(self.C_COUNT,QColor(*self.theParent.theTheme.treeWCount)) newItem.setTextAlignment(self.C_COUNT,Qt.AlignRight) newItem.setFont(self.C_FLAGS,self.fontFlags) diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 79952289..d69f50ee 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -27,15 +27,17 @@ class GuiMainStatus(QStatusBar): logger.debug("Initialising GuiMainStatus ...") - self.mainConf = nw.CONFIG - self.refTime = None + self.mainConf = nw.CONFIG + self.theParent = theParent + self.refTime = None self.iconGrey = QPixmap(16,16) - self.iconGrey.fill(QColor(120,120,120)) self.iconYellow = QPixmap(16,16) - self.iconYellow.fill(QColor(120,120, 40)) self.iconGreen = QPixmap(16,16) - self.iconGreen.fill(QColor( 40,120, 0)) + + self.iconGrey.fill(QColor(*self.theParent.theTheme.statNone)) + self.iconYellow.fill(QColor(*self.theParent.theTheme.statUnsaved)) + self.iconGreen.fill(QColor(*self.theParent.theTheme.statSaved)) self.boxStats = QLabel() self.boxStats.setToolTip("Project Word Count | Session Word Count") diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index da2ed168..9c4db5f7 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -58,7 +58,7 @@ class GuiMain(QMainWindow): self.resize(*self.mainConf.winGeometry) self._setWindowTitle() self.setWindowIcon(QIcon(path.join(self.mainConf.appRoot, nwFiles.APP_ICON))) - self.theTheme.loadTheme() + self.theTheme.updateTheme() # Main GUI Elements self.statusBar = GuiMainStatus(self) @@ -467,6 +467,7 @@ class GuiMain(QMainWindow): if dlgConf.exec_() == QDialog.Accepted: logger.debug("Applying new preferences") self.initMain() + self.theTheme.updateTheme() self.docEditor.initEditor() self.docViewer.initViewer() return True diff --git a/nw/theme.py b/nw/theme.py index f0a8db3c..0460353e 100644 --- a/nw/theme.py +++ b/nw/theme.py @@ -24,39 +24,48 @@ class Theme: def __init__(self, theParent): - self.mainConf = nw.CONFIG - self.theParent = theParent - self.cssName = "styles.css" - self.confName = "theme.conf" - self.themeList = [] + self.mainConf = nw.CONFIG + self.theParent = theParent + self.guiPath = "gui" + self.syntaxPath = "syntax" + self.cssName = "styles.css" + self.confName = "theme.conf" + self.themeList = [] + self.syntaxList = [] # Loaded Theme Settings ## Main - self.themeName = "No Name" + self.themeName = "No Name" ## Syntax - self.colText = [0,0,0] - self.colLink = [0,0,0] - self.colHead = [0,0,0] - self.colHeadH = [0,0,0] - self.colEmph = [0,0,0] - self.colDialN = [0,0,0] - self.colDialD = [0,0,0] - self.colDialS = [0,0,0] - self.colComm = [0,0,0] - self.colKey = [0,0,0] - self.colVal = [0,0,0] - self.colSpell = [0,0,0] - self.colTagErr = [0,0,0] - self.colRepTag = [0,0,0] + self.colText = [ 0, 0, 0] + self.colLink = [ 0, 0, 0] + self.colHead = [ 0, 0, 0] + self.colHeadH = [ 0, 0, 0] + self.colEmph = [ 0, 0, 0] + self.colDialN = [ 0, 0, 0] + self.colDialD = [ 0, 0, 0] + self.colDialS = [ 0, 0, 0] + self.colComm = [ 0, 0, 0] + self.colKey = [ 0, 0, 0] + self.colVal = [ 0, 0, 0] + self.colSpell = [ 0, 0, 0] + self.colTagErr = [ 0, 0, 0] + self.colRepTag = [ 0, 0, 0] + + ## GUI + self.treeWCount = [ 0, 0, 0] + self.statNone = [120,120,120] + self.statUnsaved = [120,120, 40] + self.statSaved = [ 40,120, 0] # Changeable Settings - self.guiTheme = None - self.themePath = None - self.confFile = None - self.cssFile = None - self.cssData = "" + self.guiTheme = None + self.guiSyntax = None + self.themeRoot = None + self.cssFile = None + self.cssData = "" self.updateTheme() @@ -67,10 +76,20 @@ class Theme: ## def updateTheme(self): - self.guiTheme = self.mainConf.guiTheme - self.themePath = path.join(self.mainConf.themePath, self.guiTheme) - self.confFile = path.join(self.themePath,self.confName) - self.cssFile = path.join(self.themePath,self.cssName) + + self.guiTheme = self.mainConf.guiTheme + self.guiSyntax = self.mainConf.guiSyntax + self.themeRoot = self.mainConf.themeRoot + self.themePath = path.join(self.mainConf.themeRoot, self.guiPath, self.guiTheme) + self.syntaxFile = path.join(self.themeRoot,self.syntaxPath,self.guiSyntax+".conf") + self.confFile = path.join(self.themePath,self.confName) + self.cssFile = path.join(self.themePath,self.cssName) + + print(self.guiSyntax) + + self.loadTheme() + self.loadSyntax() + return True def loadTheme(self): @@ -86,12 +105,15 @@ class Theme: logger.error("Could not load theme css file") return False - # Color Config + return True + + def loadSyntax(self): + confParser = configparser.ConfigParser() try: - confParser.read_file(open(self.confFile)) + confParser.read_file(open(self.syntaxFile)) except Exception as e: - logger.error("Could not load theme config file") + logger.error("Could not load syntax colours from: %s" % self.syntaxFile) return False ## Main @@ -117,6 +139,16 @@ class Theme: self.colTagErr = self._loadColour(confParser,cnfSec,"tagerror") self.colRepTag = self._loadColour(confParser,cnfSec,"replacetag") + ## GUI + cnfSec = "GUI" + if confParser.has_section(cnfSec): + self.treeWCount = self._loadColour(confParser,cnfSec,"treewordcount") + self.statNone = self._loadColour(confParser,cnfSec,"statusnone") + self.statUnsaved = self._loadColour(confParser,cnfSec,"statusunsaved") + self.statSaved = self._loadColour(confParser,cnfSec,"statussaved") + + logger.info("Loaded syntax colour theme '%s'" % self.guiSyntax) + return True def listThemes(self): @@ -125,14 +157,14 @@ class Theme: return self.themeList confParser = configparser.ConfigParser() - for themeDir in listdir(self.mainConf.themeRoot): - themeConf = path.join(self.mainConf.themeRoot, themeDir, self.confName) + for themeDir in listdir(path.join(self.mainConf.themeRoot, self.guiPath)): + themeConf = path.join(self.mainConf.themeRoot, self.guiPath, themeDir, self.confName) logger.verbose("Checking theme config for '%s'" % themeDir) try: confParser.read_file(open(themeConf)) except Exception as e: self.theParent.makeAlert(["Could not load theme config file",str(e)],nwAlert.ERROR) - return [] + continue themeName = "" if confParser.has_section("Main"): themeName = confParser.get("Main","name") @@ -142,6 +174,32 @@ class Theme: return self.themeList + def listSyntax(self): + + if len(self.syntaxList) > 0: + return self.syntaxList + + confParser = configparser.ConfigParser() + syntaxDir = path.join(self.mainConf.themeRoot, self.syntaxPath) + for syntaxFile in listdir(syntaxDir): + syntaxPath = path.join(syntaxDir, syntaxFile) + if not path.isfile(syntaxPath): + continue + logger.verbose("Checking theme syntax for '%s'" % syntaxFile) + try: + confParser.read_file(open(syntaxPath)) + except Exception as e: + self.theParent.makeAlert(["Could not load syntax file",str(e)],nwAlert.ERROR) + return [] + syntaxName = "" + if confParser.has_section("Main"): + syntaxName = confParser.get("Main","name") + if len(syntaxFile) > 5 and syntaxName != "": + self.syntaxList.append((syntaxFile[:-5], syntaxName)) + logger.verbose("Syntax name is '%s'" % syntaxName) + + return self.syntaxList + ## # Internal Functions ## diff --git a/nw/themes/default_light/styles.css b/nw/themes/default_light/styles.css deleted file mode 100644 index 90ef623f..00000000 --- a/nw/themes/default_light/styles.css +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Default Theme: Light - */ - -QTreeView, QHeaderView { - font-size: 13px; -} diff --git a/nw/themes/default_light/theme.conf b/nw/themes/default_light/theme.conf deleted file mode 100644 index b3d0d5be..00000000 --- a/nw/themes/default_light/theme.conf +++ /dev/null @@ -1,18 +0,0 @@ -[Main] -name = Default Light - -[Syntax] -text = 0, 0, 0 -link = 0, 0, 0 -headertext = 0, 0, 0 -headertag = 0, 0, 0 -emphasis = 0, 0, 0 -straightquotes = 0, 0, 0 -doublequotes = 0, 0, 0 -singlequotes = 0, 0, 0 -hidden = 50, 50, 50 -keyword = 0, 0, 0 -value = 0, 0, 0 -spellcheckline = 0, 0, 0 -tagerror = 0, 0, 0 -replacetag = 0, 0, 0 diff --git a/nw/themes/gui/default/styles.css b/nw/themes/gui/default/styles.css new file mode 100644 index 00000000..bb0961ac --- /dev/null +++ b/nw/themes/gui/default/styles.css @@ -0,0 +1,17 @@ +/** + * Default Theme: Light + */ + +QTextEdit { + background-color: #ffffff; + color: #141414; +} + +QTextBrowser { + background-color: #ffffff; + color: #141414; +} + +QTreeView, QHeaderView { + font-size: 13px; +} diff --git a/nw/themes/gui/default/theme.conf b/nw/themes/gui/default/theme.conf new file mode 100644 index 00000000..a8248dff --- /dev/null +++ b/nw/themes/gui/default/theme.conf @@ -0,0 +1,3 @@ +[Main] + +name = Default System Theme diff --git a/nw/themes/syntax/base16_tomorrow_night.conf b/nw/themes/syntax/base16_tomorrow_night.conf new file mode 100644 index 00000000..b1a32176 --- /dev/null +++ b/nw/themes/syntax/base16_tomorrow_night.conf @@ -0,0 +1,44 @@ +## +# Theme: Base16 Tomorrow Night +# Source: https://github.com/chriskempson/tomorrow-theme +# Credit: Chris Kempson +## +# Colours: +# Background = 1d1f21 : 29, 31, 33 +# Current Line = 282a2e : 40, 42, 46 +# Selection = 373b41 : 55, 59. 65 +# Foreground = c5c8c6 : 197, 200, 198 +# Comment = 969896 : 150, 152, 150 +# Red = cc6666 : 204, 102, 102 +# Orange = de935f : 222, 147, 95 +# Yellow = f0c674 : 240, 198, 116 +# Green = b5bd68 : 181, 189, 104 +# Aqua = 8abeb7 : 138, 190, 183 +# Blue = 81a2be : 129, 162, 190 +# Purple = b294bb : 178, 148, 187 +## + +[Main] +name = Base16 Tomorrow Night + +[Syntax] +text = 197, 200, 198 +link = 129, 162, 190 +headertext = 129, 162, 190 +headertag = 129, 162, 190 +emphasis = 222, 147, 95 +straightquotes = 204, 102, 102 +doublequotes = 181, 189, 104 +singlequotes = 240, 198, 116 +hidden = 150, 152, 150 +keyword = 204, 102, 102 +value = 178, 148, 187 +spellcheckline = 204, 102, 102 +tagerror = 181, 189, 104 +replacetag = 138, 190, 183 + +[GUI] +treewordcount = 197, 200, 198 +statusnone = 150, 152, 150 +statussaved = 39, 135, 78 +statusunsaved = 138, 32, 32 diff --git a/nw/themes/default_dark/theme.conf b/nw/themes/syntax/default_dark.conf similarity index 100% rename from nw/themes/default_dark/theme.conf rename to nw/themes/syntax/default_dark.conf diff --git a/nw/themes/syntax/default_light.conf b/nw/themes/syntax/default_light.conf new file mode 100644 index 00000000..ded3521b --- /dev/null +++ b/nw/themes/syntax/default_light.conf @@ -0,0 +1,18 @@ +[Main] +name = Default Light + +[Syntax] +text = 0, 0, 0 +link = 0, 0, 200 +headertext = 0, 100, 00 +headertag = 50, 100, 50 +emphasis = 150, 110, 30 +straightquotes = 200, 0, 0 +doublequotes = 0, 0, 200 +singlequotes = 0, 0, 200 +hidden = 100, 100, 100 +keyword = 200, 50, 50 +value = 50, 150, 50 +spellcheckline = 200, 0, 0 +tagerror = 0, 150, 0 +replacetag = 0, 150, 0