#!/usr/bin/env python # --!-- coding: utf8 --!-- # Lots of stuff from here comes from the excellet focuswriter. import os import re from PyQt5.QtCore import QSettings, QRect, QSize, Qt, QPoint, QFile, QIODevice, QTextStream from PyQt5.QtGui import QPixmap, QPainter, QColor, QBrush, QImage, QTextBlockFormat, QTextCharFormat, QFont, qGray from PyQt5.QtWidgets import qApp, QFrame from manuskript.functions import allPaths, appPath, findBackground, findFirstFile from manuskript.ui.views.textEditView import textEditView def loadThemeDatas(themeFile): settings = QSettings(themeFile, QSettings.IniFormat) _themeData = {} # Theme name _themeData["Name"] = getThemeName(themeFile) # Window Background loadThemeSetting(_themeData, settings, "Background/Color", "#000000") loadThemeSetting(_themeData, settings, "Background/ImageFile", "") loadThemeSetting(_themeData, settings, "Background/Type", 0) # Text Background loadThemeSetting(_themeData, settings, "Foreground/Color", "#ffffff") loadThemeSetting(_themeData, settings, "Foreground/Opacity", 50) loadThemeSetting(_themeData, settings, "Foreground/Margin", 40) loadThemeSetting(_themeData, settings, "Foreground/Padding", 10) loadThemeSetting(_themeData, settings, "Foreground/Position", 1) loadThemeSetting(_themeData, settings, "Foreground/Rounding", 5) loadThemeSetting(_themeData, settings, "Foreground/Width", 700) # Text Options loadThemeSetting(_themeData, settings, "Text/Color", "#ffffff") loadThemeSetting(_themeData, settings, "Text/Font", qApp.font().toString()) loadThemeSetting(_themeData, settings, "Text/Misspelled", "#ff0000") # Paragraph Options loadThemeSetting(_themeData, settings, "Spacings/Alignment", 0) loadThemeSetting(_themeData, settings, "Spacings/IndentFirstLine", False) loadThemeSetting(_themeData, settings, "Spacings/LineSpacing", 100) loadThemeSetting(_themeData, settings, "Spacings/ParagraphAbove", 0) loadThemeSetting(_themeData, settings, "Spacings/ParagraphBelow", 0) loadThemeSetting(_themeData, settings, "Spacings/TabWidth", 48) return _themeData def loadThemeSetting(datas, settings, key, default): if settings.contains(key): if type(default) != type(True): datas[key] = type(default)(settings.value(key)) else: # Bools are stored as "true"/"false", but both are read as True # since they are non-empty string. datas[key] = True if settings.value(key) == "true" else False else: datas[key] = default def getThemeName(theme): settings = QSettings(theme, QSettings.IniFormat) if settings.contains("Name"): return settings.value("Name") else: return os.path.splitext(os.path.split(theme)[1])[0] def themeTextRect(themeDatas, screenRect): margin = themeDatas["Foreground/Margin"] x = 0 y = margin width = min(themeDatas["Foreground/Width"], screenRect.width() - 2 * margin) height = screenRect.height() - 2 * margin if themeDatas["Foreground/Position"] == 0: # Left x = margin elif themeDatas["Foreground/Position"] == 1: # Center x = (screenRect.width() - width) / 2 elif themeDatas["Foreground/Position"] == 2: # Right x = screenRect.width() - margin - width elif themeDatas["Foreground/Position"] == 3: # Stretched x = margin width = screenRect.width() - 2 * margin return QRect(x, y, width, height) def createThemePreview(theme, screenRect, size=QSize(200, 120)): if type(theme) == str and os.path.exists(theme): # Theme is the path to an ini file themeDatas = loadThemeDatas(theme) else: themeDatas = theme pixmap = generateTheme(themeDatas, screenRect) addThemePreviewText(pixmap, themeDatas, screenRect) px = QPixmap(pixmap).scaled(size, Qt.KeepAspectRatio) w = px.width() / 10 h = px.height() / 10 r = themeTextRect(themeDatas, screenRect) painter = QPainter(px) painter.drawPixmap(QRect(w, h, w * 4, h * 5), pixmap, QRect(r.topLeft() - QPoint(w / 3, h / 3), QSize(w * 4, h * 5))) painter.setPen(Qt.white) painter.drawRect(QRect(w, h, w * 4, h * 5)) painter.end() return px def findThemePath(themeName): p = findFirstFile(re.escape("{}.theme".format(themeName)), "resources/themes") if not p: return findFirstFile(".*\.theme", "resources/themes") else: return p def generateTheme(themeDatas, screenRect): # Window Background px = QPixmap(screenRect.size()) px.fill(QColor(themeDatas["Background/Color"])) painter = QPainter(px) if themeDatas["Background/ImageFile"]: path = findBackground(themeDatas["Background/ImageFile"]) _type = themeDatas["Background/Type"] if path and _type > 0: if _type == 1: # Tiled painter.fillRect(screenRect, QBrush(QImage(path))) else: img = QImage(path) scaled = img.size() if _type == 3: # Stretched scaled.scale(screenRect.size(), Qt.IgnoreAspectRatio) elif _type == 4: # Scaled scaled.scale(screenRect.size(), Qt.KeepAspectRatio) elif _type == 5: # Zoomed scaled.scale(screenRect.size(), Qt.KeepAspectRatioByExpanding) painter.drawImage((screenRect.width() - scaled.width()) / 2, (screenRect.height() - scaled.height()) / 2, img.scaled(scaled)) # Text Background textRect = themeTextRect(themeDatas, screenRect) painter.save() color = QColor(themeDatas["Foreground/Color"]) color.setAlpha(themeDatas["Foreground/Opacity"] * 255 / 100) painter.setBrush(color) painter.setPen(Qt.NoPen) r = themeDatas["Foreground/Rounding"] painter.drawRoundedRect(textRect, r, r) painter.restore() painter.end() return px def themeEditorGeometry(themeDatas, textRect): padding = themeDatas["Foreground/Padding"] x = textRect.x() + padding y = textRect.y() + padding + themeDatas["Spacings/ParagraphAbove"] width = textRect.width() - 2 * padding height = textRect.height() - 2 * padding - themeDatas["Spacings/ParagraphAbove"] return x, y, width, height def getThemeBlockFormat(themeDatas): bf = QTextBlockFormat() bf.setAlignment(Qt.AlignLeft if themeDatas["Spacings/Alignment"] == 0 else Qt.AlignCenter if themeDatas["Spacings/Alignment"] == 1 else Qt.AlignRight if themeDatas["Spacings/Alignment"] == 2 else Qt.AlignJustify) bf.setLineHeight(themeDatas["Spacings/LineSpacing"], QTextBlockFormat.ProportionalHeight) bf.setTextIndent(themeDatas["Spacings/TabWidth"] * 1 if themeDatas["Spacings/IndentFirstLine"] else 0) bf.setTopMargin(themeDatas["Spacings/ParagraphAbove"]) bf.setBottomMargin(themeDatas["Spacings/ParagraphBelow"]) return bf def setThemeEditorDatas(editor, themeDatas, pixmap, screenRect): textRect = themeTextRect(themeDatas, screenRect) x, y, width, height = themeEditorGeometry(themeDatas, textRect) editor.setGeometry(x, y, width, height) # p = editor.palette() ##p.setBrush(QPalette.Base, QBrush(pixmap.copy(x, y, width, height))) # p.setBrush(QPalette.Base, QColor(Qt.transparent)) # p.setColor(QPalette.Text, QColor(themeDatas["Text/Color"])) # p.setColor(QPalette.Highlight, QColor(themeDatas["Text/Color"])) # p.setColor(QPalette.HighlightedText, Qt.black if qGray(QColor(themeDatas["Text/Color"]).rgb()) > 127 else Qt.white) # editor.setPalette(p) editor.setAttribute(Qt.WA_NoSystemBackground, True) bf = getThemeBlockFormat(themeDatas) editor.setDefaultBlockFormat(bf) # b = editor.document().firstBlock() # cursor = editor.textCursor() # cursor.setBlockFormat(bf) # while b.isValid(): # bf2 = b.blockFormat() # bf2.merge(bf) # cursor.setPosition(b.position()) ##cursor.setPosition(b.position(), QTextCursor.KeepAnchor) # cursor.setBlockFormat(bf2) # b = b.next() editor.setTabStopWidth(themeDatas["Spacings/TabWidth"]) editor.document().setIndentWidth(themeDatas["Spacings/TabWidth"]) editor.highlighter.setMisspelledColor(QColor(themeDatas["Text/Misspelled"])) cf = QTextCharFormat() # f = QFont() # f.fromString(themeDatas["Text/Font"]) # cf.setFont(f) editor.highlighter.setDefaultCharFormat(cf) f = QFont() f.fromString(themeDatas["Text/Font"]) # editor.setFont(f) editor.setStyleSheet(""" background: transparent; color: {foreground}; font-family: {ff}; font-size: {fs}; selection-color: {sc}; selection-background-color: {sbc}; """.format( foreground=themeDatas["Text/Color"], ff=f.family(), fs="{}pt".format(str(f.pointSize())), sc="black" if qGray(QColor(themeDatas["Text/Color"]).rgb()) > 127 else "white", sbc=themeDatas["Text/Color"], ) ) editor._fromTheme = True def addThemePreviewText(pixmap, themeDatas, screenRect): # Text previewText = textEditView(highlighting=True) previewText.setFrameStyle(QFrame.NoFrame) previewText.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) previewText.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) f = QFile(appPath("resources/themes/preview.txt")) f.open(QIODevice.ReadOnly) previewText.setPlainText(QTextStream(f).readAll()) setThemeEditorDatas(previewText, themeDatas, pixmap, screenRect) previewText.render(pixmap, previewText.pos()) ## Text Background ##themeDatas["Foreground/Color"] ##themeDatas["Foreground/Opacity"] ##themeDatas["Foreground/Margin"] ##themeDatas["Foreground/Padding"] ##themeDatas["Foreground/Position"] ##themeDatas["Foreground/Rounding"] ##themeDatas["Foreground/Width"] ## Text Options ##themeDatas["Text/Color"] ##themeDatas["Text/Font"] # themeDatas["Text/Misspelled"] ## Paragraph Options ##themeDatas["Spacings/IndentFirstLine"] ##themeDatas["Spacings/LineSpacing"] ##themeDatas["Spacings/ParagraphAbove"] ##themeDatas["Spacings/ParagraphBelow"] ##themeDatas["Spacings/TabWidth"]