mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-09-29 08:51:14 +13:00
dc86e3b14e
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
306 lines
11 KiB
Python
306 lines
11 KiB
Python
#!/usr/bin/env python
|
|
# --!-- coding: utf8 --!--
|
|
|
|
# Lots of stuff from here comes from the excellent 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.MDEditView import MDEditView
|
|
|
|
_thumbCache = {}
|
|
|
|
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):
|
|
"""
|
|
Loads data from ini file, using default value if the key is absent,
|
|
and casting to the proper type based on default.
|
|
"""
|
|
datas[key] = settings.value(key, default, type(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(int(x), int(y), int(width), int(height))
|
|
|
|
|
|
def createThemePreview(theme, screenRect, size=QSize(200, 120)):
|
|
"""
|
|
Generates a QPixmap preview for given theme.
|
|
|
|
Theme can be either a string containing the filename of the ini
|
|
file with the theme settings, or it can be a dict with the settings.
|
|
|
|
If theme is a filename, the result is cached.
|
|
"""
|
|
|
|
# Checking whether theme is a string or dict
|
|
if type(theme) == str and os.path.exists(theme):
|
|
# Theme is the path to an ini file
|
|
themeDatas = loadThemeDatas(theme)
|
|
fromFile = True
|
|
else:
|
|
themeDatas = theme
|
|
fromFile = False
|
|
|
|
# Check if item is in cache
|
|
if fromFile and theme in _thumbCache:
|
|
if _thumbCache[theme][0] == themeDatas:
|
|
return _thumbCache[theme][1]
|
|
|
|
pixmap = generateTheme(themeDatas, screenRect)
|
|
|
|
addThemePreviewText(pixmap, themeDatas, screenRect)
|
|
|
|
px = QPixmap(pixmap).scaled(size, Qt.KeepAspectRatio)
|
|
|
|
w = int(px.width() / 10)
|
|
h = int(px.height() / 10)
|
|
r = themeTextRect(themeDatas, screenRect)
|
|
|
|
painter = QPainter(px)
|
|
painter.drawPixmap(QRect(w, h, w * 4, h * 5), pixmap,
|
|
QRect(r.topLeft() - QPoint(int(w / 3), int(h / 3)), QSize(w * 4, h * 5)))
|
|
painter.setPen(Qt.white)
|
|
painter.drawRect(QRect(w, h, w * 4, h * 5))
|
|
painter.end()
|
|
|
|
# If theme is a themefile, we keep it in cache
|
|
if fromFile:
|
|
_thumbCache[theme] = [themeDatas, px]
|
|
|
|
return px
|
|
|
|
|
|
def findThemePath(themeName):
|
|
p = findFirstFile(re.escape("{}.theme".format(themeName)), "resources/themes")
|
|
if not p:
|
|
return findFirstFile(r".*\.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(int((screenRect.width() - scaled.width()) / 2),
|
|
int((screenRect.height() - scaled.height()) / 2), img.scaled(scaled))
|
|
|
|
# Text Background
|
|
textRect = themeTextRect(themeDatas, screenRect)
|
|
|
|
painter.save()
|
|
color = QColor(themeDatas["Foreground/Color"])
|
|
color.setAlpha(int(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("""
|
|
QTextEdit {{
|
|
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
|
|
editor._themeData = themeDatas
|
|
editor.highlighter.updateColorScheme()
|
|
|
|
def addThemePreviewText(pixmap, themeDatas, screenRect):
|
|
# Text
|
|
previewText = MDEditView(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"]
|