aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/defconQt/__main__.py5
-rw-r--r--Lib/defconQt/baseCodeEditor.py159
-rw-r--r--Lib/defconQt/featureTextEditor.py230
-rw-r--r--Lib/defconQt/fontView.py31
-rw-r--r--Lib/defconQt/scriptingWindow.py94
5 files changed, 337 insertions, 182 deletions
diff --git a/Lib/defconQt/__main__.py b/Lib/defconQt/__main__.py
index ffcef50..b656bef 100644
--- a/Lib/defconQt/__main__.py
+++ b/Lib/defconQt/__main__.py
@@ -1,10 +1,9 @@
from defconQt.objects.defcon import TFont
-from defconQt.fontView import MainWindow
+from defconQt.fontView import Application, MainWindow
import sys
import os
from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QApplication
if len(sys.argv) < 2:
share_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'share')
@@ -20,7 +19,7 @@ else:
from defconQt import representationFactories
representationFactories.registerAllFactories()
#with PyCallGraph(output=GraphvizOutput()):
-app = QApplication(sys.argv)
+app = Application(sys.argv)
# TODO: http://stackoverflow.com/a/21330349/2037879
app.setWindowIcon(QIcon("defconQt/resources/icon.png"))
window = MainWindow(TFont(os.path.abspath(ufoFile)))
diff --git a/Lib/defconQt/baseCodeEditor.py b/Lib/defconQt/baseCodeEditor.py
new file mode 100644
index 0000000..cab9ce9
--- /dev/null
+++ b/Lib/defconQt/baseCodeEditor.py
@@ -0,0 +1,159 @@
+from PyQt5.QtCore import QRegularExpression, Qt
+from PyQt5.QtGui import QColor, QFont, QPainter, QSyntaxHighlighter, QTextCursor
+from PyQt5.QtWidgets import QPlainTextEdit, QWidget
+
+# maybe add closeEvent
+
+class LineNumberArea(QWidget):
+ def __init__(self, editor):
+ super(LineNumberArea, self).__init__(editor)
+
+ def sizeHint(self):
+ return QSize(self.parent().lineNumberAreaWidth(), 0)
+
+ def paintEvent(self, event):
+ self.parent().lineNumberAreaPaintEvent(event)
+
+class CodeEditor(QPlainTextEdit):
+ def __init__(self, text=None, parent=None):
+ super(CodeEditor, self).__init__(parent)
+ # https://gist.github.com/murphyrandle/2921575
+ font = QFont('Roboto Mono', 10)
+ font.setFixedPitch(True)
+ self.setFont(font)
+
+ self.indent = " "
+ self.openBlockDelimiter = None
+ self.lineNumbers = LineNumberArea(self)
+ self.setPlainText(text)
+ # kick-in geometry update before arming signals bc blockCountChanged
+ # won't initially trigger if text is None or one-liner.
+ self.updateLineNumberAreaWidth()
+ self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
+ self.updateRequest.connect(self.updateLineNumberArea)
+
+ def lineNumberAreaPaintEvent(self, event):
+ painter = QPainter(self.lineNumbers)
+ painter.fillRect(event.rect(), QColor(230, 230, 230))
+ d = event.rect().topRight()
+ a = event.rect().bottomRight()
+ painter.setPen(Qt.darkGray)
+ painter.drawLine(d.x(), d.y(), a.x(), a.y())
+ painter.setPen(QColor(100, 100, 100))
+ painter.setFont(self.font())
+
+ block = self.firstVisibleBlock()
+ blockNumber = block.blockNumber()
+ top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
+ bottom = top + int(self.blockBoundingRect(block).height())
+
+ while block.isValid() and top <= event.rect().bottom():
+ if block.isVisible() and bottom >= event.rect().top():
+ number = str(blockNumber + 1)
+ painter.drawText(4, top, self.lineNumbers.width() - 8,
+ self.fontMetrics().height(), Qt.AlignRight, number)
+ block = block.next()
+ top = bottom
+ bottom = top + int(self.blockBoundingRect(block).height())
+ blockNumber += 1
+
+ def lineNumberAreaWidth(self):
+ digits = 1
+ top = max(1, self.blockCount())
+ while top >= 10:
+ top /= 10
+ digits += 1
+ # Avoid too frequent geometry changes
+ if digits < 3: digits = 3
+ return 10 + self.fontMetrics().width('9') * digits
+
+ def updateLineNumberArea(self, rect, dy):
+ if dy:
+ self.lineNumbers.scroll(0, dy)
+ else:
+ self.lineNumbers.update(0, rect.y(), self.lineNumbers.width(),
+ rect.height())
+
+ if rect.contains(self.viewport().rect()):
+ self.updateLineNumberAreaWidth(0)
+
+ def updateLineNumberAreaWidth(self, newBlockCount=None):
+ self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
+
+ def resizeEvent(self, event):
+ super(CodeEditor, self).resizeEvent(event)
+ cr = self.contentsRect()
+ self.lineNumbers.setGeometry(cr.left(), cr.top(),
+ self.lineNumberAreaWidth(), cr.height())
+
+ def findLineIndentLevel(self, cursor):
+ indent = 0
+ cursor.select(QTextCursor.LineUnderCursor)
+ lineLength = len(cursor.selectedText()) // len(self.indent)
+ cursor.movePosition(QTextCursor.StartOfLine)
+ while lineLength > 0:
+ cursor.movePosition(QTextCursor.NextCharacter,
+ QTextCursor.KeepAnchor, len(self.indent))
+ if cursor.selectedText() == self.indent:
+ indent += 1
+ else: break
+ # Now move the anchor back to the position()
+ #cursor.movePosition(QTextCursor.NoMove) # shouldn't NoMove work here?
+ cursor.setPosition(cursor.position())
+ lineLength -= 1
+ cursor.movePosition(QTextCursor.EndOfLine)
+ return indent
+
+ def keyPressEvent(self, event):
+ key = event.key()
+ if key == Qt.Key_Return:
+ cursor = self.textCursor()
+ indentLvl = self.findLineIndentLevel(cursor)
+ if self.openBlockDelimiter is not None:
+ cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor)
+ if cursor.selectedText() == self.openBlockDelimiter:
+ indentLvl += 1
+ super(CodeEditor, self).keyPressEvent(event)
+ newLineSpace = "".join(self.indent for _ in range(indentLvl))
+ cursor = self.textCursor()
+ cursor.insertText(newLineSpace)
+ elif key == Qt.Key_Tab:
+ cursor = self.textCursor()
+ cursor.insertText(self.indent)
+ elif key == Qt.Key_Backspace:
+ cursor = self.textCursor()
+ cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor,
+ len(self.indent))
+ if cursor.selectedText() == self.indent:
+ cursor.removeSelectedText()
+ else:
+ super(CodeEditor, self).keyPressEvent(event)
+ else:
+ super(CodeEditor, self).keyPressEvent(event)
+
+ def wheelEvent(self, event):
+ if event.modifiers() & Qt.ControlModifier:
+ font = self.font()
+ newPointSize = font.pointSize() + event.angleDelta().y() / 120.0
+ event.accept()
+ if newPointSize < 6: return
+ font.setPointSize(newPointSize)
+ self.setFont(font)
+ else:
+ super(CodeEditor, self).wheelEvent(event)
+
+class CodeHighlighter(QSyntaxHighlighter):
+ def __init__(self, parent=None):
+ super(CodeHighlighter, self).__init__(parent)
+ self.highlightingRules = []
+
+ def highlightBlock(self, text):
+ for pattern, fmt in self.highlightingRules:
+ regex = QRegularExpression(pattern)
+ i = regex.globalMatch(text)
+ while i.hasNext():
+ match = i.next()
+ start = match.capturedStart()
+ length = match.capturedLength()
+ self.setFormat(start, length, fmt)
+ self.setCurrentBlockState(0)
diff --git a/Lib/defconQt/featureTextEditor.py b/Lib/defconQt/featureTextEditor.py
index 5ee9b02..5c5de76 100644
--- a/Lib/defconQt/featureTextEditor.py
+++ b/Lib/defconQt/featureTextEditor.py
@@ -1,7 +1,9 @@
-from PyQt5.QtCore import QFile, QRegExp, Qt
-from PyQt5.QtGui import QColor, QFont, QKeySequence, QPainter, QSyntaxHighlighter, QTextCharFormat, QTextCursor
+from defconQt.baseCodeEditor import CodeEditor, CodeHighlighter
+from PyQt5.QtCore import QFile, Qt
+from PyQt5.QtGui import (QColor, QFont, QKeySequence, QPainter, QTextCharFormat,
+ QTextCursor)
from PyQt5.QtWidgets import (QApplication, QFileDialog, QMainWindow, QMenu,
- QMessageBox, QPlainTextEdit, QWidget)
+ QMessageBox, QPlainTextEdit, QWidget)
# TODO: implement search and replace
class MainEditWindow(QMainWindow):
@@ -9,14 +11,20 @@ class MainEditWindow(QMainWindow):
super(MainEditWindow, self).__init__(parent)
self.font = font
- self.setupFileMenu()
- self.editor = TextEditor(self.font.features.text, self)
+ self.editor = FeatureTextEditor(self.font.features.text, self)
self.resize(600, 500)
+ fileMenu = QMenu("&File", self)
+ fileMenu.addAction("&Save...", self.save, QKeySequence.Save)
+ fileMenu.addSeparator()
+ fileMenu.addAction("Reload from UFO", self.reload)
+ fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
+ self.menuBar().addMenu(fileMenu)
+
self.setCentralWidget(self.editor)
self.setWindowTitle("Font features", self.font)
# now arm `undoAvailable` to `setWindowModified`
- self.editor.setFileChangedCallback(self.setWindowModified)
+ self.editor.undoAvailable.connect(self.setWindowModified)
def setWindowTitle(self, title, font):
if font is not None: puts = "[*]%s – %s %s" % (title, self.font.info.familyName, self.font.info.styleName)
@@ -45,126 +53,15 @@ class MainEditWindow(QMainWindow):
def save(self):
self.editor.write(self.font.features)
- def setupFileMenu(self):
- fileMenu = QMenu("&File", self)
- self.menuBar().addMenu(fileMenu)
-
- fileMenu.addAction("&Save...", self.save, QKeySequence.Save)
- fileMenu.addSeparator()
- fileMenu.addAction("Reload from UFO", self.reload)
- fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
-
-class LineNumberArea(QWidget):
- def __init__(self, editor):
- super(LineNumberArea, self).__init__(editor)
-
- def sizeHint(self):
- return QSize(self.parent().lineNumberAreaWidth(), 0)
-
- def paintEvent(self, event):
- self.parent().lineNumberAreaPaintEvent(event)
-
-class TextEditor(QPlainTextEdit):
+class FeatureTextEditor(CodeEditor):
def __init__(self, text=None, parent=None):
- super(TextEditor, self).__init__(parent)
- # https://gist.github.com/murphyrandle/2921575
- font = QFont('Roboto Mono', 10)
- font.setFixedPitch(True)
- self.setFont(font)
-
- self._indent = " "
- self.highlighter = Highlighter(self.document())
- self.lineNumbers = LineNumberArea(self)
- self.setPlainText(text)
- # kick-in geometry update before arming signals bc blockCountChanged
- # won't initially trigger if text is None or one-liner.
- self.updateLineNumberAreaWidth()
- self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
- self.updateRequest.connect(self.updateLineNumberArea)
-
- def setFontParams(self, family='Roboto Mono', ptSize=10, isMono=True):
- font = QFont(family, ptSize)
- font.setFixedPitch(isMono)
- self.setFont(font)
-
- # TODO: add way to unset callback?
- def setFileChangedCallback(self, fileChangedCallback):
- self.undoAvailable.connect(fileChangedCallback)
+ super(FeatureTextEditor, self).__init__(text, parent)
+ self.openBlockDelimiter = '{'
+ self.highlighter = FeatureTextHighlighter(self.document())
def write(self, features):
features.text = self.toPlainText()
- def lineNumberAreaPaintEvent(self, event):
- painter = QPainter(self.lineNumbers)
- painter.fillRect(event.rect(), QColor(230, 230, 230))
- d = event.rect().topRight()
- a = event.rect().bottomRight()
- painter.setPen(Qt.darkGray)
- painter.drawLine(d.x(), d.y(), a.x(), a.y())
- painter.setPen(QColor(100, 100, 100))
- painter.setFont(self.font())
-
- block = self.firstVisibleBlock()
- blockNumber = block.blockNumber();
- top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
- bottom = top + int(self.blockBoundingRect(block).height())
-
- while block.isValid() and top <= event.rect().bottom():
- if block.isVisible() and bottom >= event.rect().top():
- number = str(blockNumber + 1)
- painter.drawText(4, top, self.lineNumbers.width() - 8,
- self.fontMetrics().height(),
- Qt.AlignRight, number)
- block = block.next()
- top = bottom
- bottom = top + int(self.blockBoundingRect(block).height())
- blockNumber += 1
-
- def lineNumberAreaWidth(self):
- digits = 1
- top = max(1, self.blockCount())
- while top >= 10:
- top /= 10
- digits += 1
- # Avoid too frequent geometry changes
- if digits < 3: digits = 3
- return 10 + self.fontMetrics().width('9') * digits
-
- def updateLineNumberArea(self, rect, dy):
- if dy:
- self.lineNumbers.scroll(0, dy);
- else:
- self.lineNumbers.update(0, rect.y(),
- self.lineNumbers.width(), rect.height())
-
- if rect.contains(self.viewport().rect()):
- self.updateLineNumberAreaWidth(0)
-
- def updateLineNumberAreaWidth(self, newBlockCount=None):
- self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
-
- def resizeEvent(self, event):
- super(TextEditor, self).resizeEvent(event)
- cr = self.contentsRect()
- self.lineNumbers.setGeometry(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height())
-
- def findLineIndentLevel(self, cursor):
- indent = 0
- cursor.select(QTextCursor.LineUnderCursor)
- lineLength = len(cursor.selectedText()) // len(self._indent)
- cursor.movePosition(QTextCursor.StartOfLine)
- while lineLength > 0:
- cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, len(self._indent))
- if cursor.selectedText() == self._indent:
- indent += 1
- else: break
- # Now move the anchor back to the position()
- #cursor.movePosition(QTextCursor.NoMove) # shouldn't NoMove work here?
- cursor.setPosition(cursor.position())
- lineLength -= 1
- cursor.movePosition(QTextCursor.EndOfLine)
- return indent
-
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Return:
@@ -173,7 +70,7 @@ class TextEditor(QPlainTextEdit):
newBlock = False
cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor)
- if cursor.selectedText() == '{':
+ if cursor.selectedText() == self.openBlockDelimiter:
# We don't add a closing tag if there is text right below with the same
# indentation level because in that case the user might just be looking
# to add a new line
@@ -191,7 +88,7 @@ class TextEditor(QPlainTextEdit):
if newBlock:
txt = cursor.selectedText().lstrip(" ").split(" ")
if len(txt) > 1:
- if len(txt) < 3 and txt[-1][-1] == '{':
+ if len(txt) < 3 and txt[-1][-1] == self.openBlockDelimiter:
feature = txt[-1][:-1]
else:
feature = txt[1]
@@ -199,74 +96,49 @@ class TextEditor(QPlainTextEdit):
feature = None
cursor.movePosition(QTextCursor.EndOfLine)
- super(TextEditor, self).keyPressEvent(event)
- newLineSpace = "".join(self._indent for _ in range(indentLvl))
+ cursor.insertText("\n")
+ newLineSpace = "".join(self.indent for _ in range(indentLvl))
cursor.insertText(newLineSpace)
if newBlock:
- super(TextEditor, self).keyPressEvent(event)
- newLineSpace = "".join((newLineSpace[:-len(self._indent)], "} ", feature, ";"))
+ cursor.insertText("\n")
+ newLineSpace = "".join((newLineSpace[:-len(self.indent)], "} ", feature, ";"))
cursor.insertText(newLineSpace)
cursor.movePosition(QTextCursor.Up)
cursor.movePosition(QTextCursor.EndOfLine)
self.setTextCursor(cursor)
- elif key == Qt.Key_Tab:
- cursor = self.textCursor()
- cursor.insertText(self._indent)
- elif key == Qt.Key_Backspace:
- cursor = self.textCursor()
- cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor,
- len(self._indent))
- if cursor.selectedText() == self._indent:
- cursor.removeSelectedText()
- else:
- super(TextEditor, self).keyPressEvent(event)
else:
- super(TextEditor, self).keyPressEvent(event)
-
-keywordPatterns = ["\\bAscender\\b", "\\bAttach\\b", "\\bCapHeight\\b", "\\bCaretOffset\\b", "\\bCodePageRange\\b",
- "\\bDescender\\b", "\\bFontRevision\\b", "\\bGlyphClassDef\\b", "\\bHorizAxis.BaseScriptList\\b",
- "\\bHorizAxis.BaseTagList\\b", "\\bHorizAxis.MinMax\\b", "\\bIgnoreBaseGlyphs\\b", "\\bIgnoreLigatures\\b",
- "\\bIgnoreMarks\\b", "\\bLigatureCaretByDev\\b", "\\bLigatureCaretByIndex\\b", "\\bLigatureCaretByPos\\b",
- "\\bLineGap\\b", "\\bMarkAttachClass\\b", "\\bMarkAttachmentType\\b", "\\bNULL\\b", "\\bPanose\\b", "\\bRightToLeft\\b",
- "\\bTypoAscender\\b", "\\bTypoDescender\\b", "\\bTypoLineGap\\b", "\\bUnicodeRange\\b", "\\bUseMarkFilteringSet\\b",
- "\\bVendor\\b", "\\bVertAdvanceY\\b", "\\bVertAxis.BaseScriptList\\b", "\\bVertAxis.BaseTagList\\b",
- "\\bVertAxis.MinMax\\b", "\\bVertOriginY\\b", "\\bVertTypoAscender\\b", "\\bVertTypoDescender\\b",
- "\\bVertTypoLineGap\\b", "\\bXHeight\\b", "\\banchorDef\\b", "\\banchor\\b", "\\banonymous\\b", "\\banon\\b",
- "\\bby\\b", "\\bcontour\\b", "\\bcursive\\b", "\\bdevice\\b", "\\benumerate\\b", "\\benum\\b", "\\bexclude_dflt\\b",
- "\\bfeatureNames\\b", "\\bfeature\\b", "\\bfrom\\b", "\\bignore\\b", "\\binclude_dflt\\b", "\\binclude\\b",
- "\\blanguagesystem\\b", "\\blanguage\\b", "\\blookupflag\\b", "\\blookup\\b", "\\bmarkClass\\b", "\\bmark\\b",
- "\\bnameid\\b", "\\bname\\b", "\\bparameters\\b", "\\bposition\\b", "\\bpos\\b", "\\brequired\\b", "\\breversesub\\b",
- "\\brsub\\b", "\\bscript\\b", "\\bsizemenuname\\b", "\\bsubstitute\\b", "\\bsubtable\\b", "\\bsub\\b", "\\btable\\b",
- "\\buseExtension\\b", "\\bvalueRecordDef\\b", "\\bwinAscent\\b", "\\bwinDescent\\b"]
-
-class Highlighter(QSyntaxHighlighter):
+ super(FeatureTextEditor, self).keyPressEvent(event)
+
+keywordPatterns = ["Ascender", "Attach", "CapHeight", "CaretOffset", "CodePageRange",
+ "Descender", "FontRevision", "GlyphClassDef", "HorizAxis.BaseScriptList",
+ "HorizAxis.BaseTagList", "HorizAxis.MinMax", "IgnoreBaseGlyphs", "IgnoreLigatures",
+ "IgnoreMarks", "LigatureCaretByDev", "LigatureCaretByIndex", "LigatureCaretByPos",
+ "LineGap", "MarkAttachClass", "MarkAttachmentType", "NULL", "Panose", "RightToLeft",
+ "TypoAscender", "TypoDescender", "TypoLineGap", "UnicodeRange", "UseMarkFilteringSet",
+ "Vendor", "VertAdvanceY", "VertAxis.BaseScriptList", "VertAxis.BaseTagList",
+ "VertAxis.MinMax", "VertOriginY", "VertTypoAscender", "VertTypoDescender",
+ "VertTypoLineGap", "XHeight", "anchorDef", "anchor", "anonymous", "anon",
+ "by", "contour", "cursive", "device", "enumerate", "enum", "exclude_dflt",
+ "featureNames", "feature", "from", "ignore", "include_dflt", "include",
+ "languagesystem", "language", "lookupflag", "lookup", "markClass", "mark",
+ "nameid", "name", "parameters", "position", "pos", "required", "reversesub",
+ "rsub", "script", "sizemenuname", "substitute", "subtable", "sub", "table",
+ "useExtension", "valueRecordDef", "winAscent", "winDescent"]
+
+class FeatureTextHighlighter(CodeHighlighter):
def __init__(self, parent=None):
- super(Highlighter, self).__init__(parent)
+ super(FeatureTextHighlighter, self).__init__(parent)
keywordFormat = QTextCharFormat()
- keywordFormat.setForeground(QColor(30, 150, 220))
+ keywordFormat.setForeground(QColor(34, 34, 34))
keywordFormat.setFontWeight(QFont.Bold)
-
- self.highlightingRules = [(QRegExp("(%s)" % ("|".join(keywordPatterns))), keywordFormat)]
+ self.highlightingRules.append(("\\b(%s)\\b" % ("|".join(keywordPatterns)), keywordFormat))
singleLineCommentFormat = QTextCharFormat()
singleLineCommentFormat.setForeground(Qt.darkGray)
- self.highlightingRules.append((QRegExp("#[^\n]*"),
- singleLineCommentFormat))
-
- classFormat = QTextCharFormat()
- classFormat.setFontWeight(QFont.Bold)
- classFormat.setForeground(QColor(200, 50, 150))
- self.highlightingRules.append((QRegExp("@[A-Za-z0-9_.]+"),
- classFormat))
-
- def highlightBlock(self, text):
- for pattern, format in self.highlightingRules:
- expression = QRegExp(pattern)
- index = expression.indexIn(text)
- while index >= 0:
- length = expression.matchedLength()
- self.setFormat(index, length, format)
- index = expression.indexIn(text, index + length)
+ self.highlightingRules.append(("#[^\n]*", singleLineCommentFormat))
- self.setCurrentBlockState(0)
+ groupFormat = QTextCharFormat()
+ groupFormat.setFontWeight(QFont.Bold)
+ groupFormat.setForeground(QColor(96, 106, 161))
+ self.highlightingRules.append(("@[A-Za-z0-9_.]+", groupFormat))
diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py
index 0ec8910..9921cdb 100644
--- a/Lib/defconQt/fontView.py
+++ b/Lib/defconQt/fontView.py
@@ -3,6 +3,7 @@ from defconQt.fontInfo import TabDialog
from defconQt.glyphCollectionView import GlyphCollectionWidget
from defconQt.glyphView import MainGfxWindow
from defconQt.groupsView import GroupsWindow
+from defconQt.scriptingWindow import MainScriptingWindow
from defconQt.objects.defcon import CharacterSet, TFont, TGlyph
from defcon import Component
from defconQt.spaceCenter import MainSpaceWindow
@@ -45,6 +46,17 @@ latin1 = CharacterSet(
"breve","dotaccent","ring","ogonek","tilde","hungarumlaut","quoteleft",
"quoteright","minus"], "Latin-1")
+class Application(QApplication):
+ def allFonts(self):
+ fonts = []
+ for window in QApplication.topLevelWidgets():
+ if isinstance(window, MainWindow):
+ fonts.append(window._font)
+ return fonts
+
+ def currentFont(self):
+ return self.currentMainWindow._font
+
# TODO: implement Frederik's Glyph Construction Builder
class AddGlyphDialog(QDialog):
def __init__(self, currentGlyphs=None, parent=None):
@@ -311,6 +323,10 @@ class MainWindow(QMainWindow):
fontMenu.addAction("Sort…", self.sortCharacters)
menuBar.addMenu(fontMenu)
+ pythonMenu = QMenu("&Python", self)
+ pythonMenu.addAction("Scripting &window", self.scripting)
+ menuBar.addMenu(pythonMenu)
+
windowMenu = QMenu("&Windows", self)
windowMenu.addAction("&Space center", self.spaceCenter, "Ctrl+Y")
windowMenu.addAction("&Groups window", self.fontGroups, "Ctrl+G")
@@ -566,6 +582,12 @@ class MainWindow(QMainWindow):
self.collectionWidget._sizeEvent(self.width(), val)
QToolTip.showText(QCursor.pos(), str(val), self)
+ def event(self, event):
+ if event.type() == QEvent.WindowActivate:
+ app = QApplication.instance()
+ app.currentMainWindow = self
+ return super(MainWindow, self).event(event)
+
def resizeEvent(self, event):
if self.isVisible(): self.collectionWidget._sizeEvent(event.size().width())
super(MainWindow, self).resizeEvent(event)
@@ -619,6 +641,15 @@ class MainWindow(QMainWindow):
else:
self.fontGroupsWindow.raise_()
+ def scripting(self):
+ # TODO: see up here
+ app = QApplication.instance()
+ if not (hasattr(app, 'scriptingWindow') and app.scriptingWindow.isVisible()):
+ app.scriptingWindow = MainScriptingWindow()
+ app.scriptingWindow.show()
+ else:
+ app.scriptingWindow.raise_()
+
def sortCharacters(self):
sortDescriptor, ok = SortDialog.getDescriptor(self, self.sortDescriptor)
if ok:
diff --git a/Lib/defconQt/scriptingWindow.py b/Lib/defconQt/scriptingWindow.py
new file mode 100644
index 0000000..2448d48
--- /dev/null
+++ b/Lib/defconQt/scriptingWindow.py
@@ -0,0 +1,94 @@
+from defconQt.baseCodeEditor import CodeEditor, CodeHighlighter
+from keyword import kwlist
+import traceback
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QColor, QFont, QKeySequence, QTextCharFormat, QTextCursor
+from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QPlainTextEdit
+
+class MainScriptingWindow(QMainWindow):
+ def __init__(self):
+ super(MainScriptingWindow, self).__init__()
+
+ self.editor = PythonEditor(parent=self)
+ self.resize(600, 500)
+
+ fileMenu = QMenu("&File", self)
+ fileMenu.addAction("&Run…", self.runScript, "Ctrl+R")
+ fileMenu.addSeparator()
+ fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
+ self.menuBar().addMenu(fileMenu)
+
+ self.setCentralWidget(self.editor)
+ self.setWindowTitle("[*]Untitled.py")
+ # arm `undoAvailable` to `setWindowModified`
+ self.editor.undoAvailable.connect(self.setWindowModified)
+
+ def runScript(self):
+ app = QApplication.instance()
+ script = self.editor.toPlainText()
+ global_vars = {
+ "__builtins__": __builtins__,
+ "AllFonts": app.allFonts,
+ "CurrentFont": app.currentFont,
+ }
+ try:
+ code = compile(script, "<string>", "exec")
+ exec(code, global_vars)
+ except:
+ print(traceback.format_exc())
+
+class PythonEditor(CodeEditor):
+ autocomplete = {
+ Qt.Key_ParenLeft: "()",
+ Qt.Key_BracketLeft: "[]",
+ Qt.Key_BraceLeft: "{}",
+ Qt.Key_Apostrophe: "''",
+ Qt.Key_QuoteDbl: '""',
+ }
+
+ def __init__(self, text=None, parent=None):
+ super(PythonEditor, self).__init__(text, parent)
+ self.openBlockDelimiter = ":"
+ self.highlighter = PythonHighlighter(self.document())
+
+ def keyPressEvent(self, event):
+ key = event.key()
+ if key in self.autocomplete.keys():
+ super(PythonEditor, self).keyPressEvent(event)
+ cursor = self.textCursor()
+ cursor.insertText(self.autocomplete[key][-1])
+ cursor.movePosition(QTextCursor.PreviousCharacter)
+ self.setTextCursor(cursor)
+ event.accept()
+ return
+ elif key == Qt.Key_Backspace:
+ cursor = self.textCursor()
+ ok = cursor.movePosition(QTextCursor.PreviousCharacter)
+ if ok:
+ ok = cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 2)
+ if ok and cursor.selectedText() in self.autocomplete.values():
+ cursor.removeSelectedText()
+ event.accept()
+ return
+ super(PythonEditor, self).keyPressEvent(event)
+
+class PythonHighlighter(CodeHighlighter):
+ def __init__(self, parent=None):
+ super(PythonHighlighter, self).__init__(parent)
+
+ keywordFormat = QTextCharFormat()
+ keywordFormat.setForeground(QColor(34, 34, 34))
+ keywordFormat.setFontWeight(QFont.Bold)
+ self.highlightingRules.append(("\\b(%s)\\b" % ("|".join(kwlist)), keywordFormat))
+
+ singleLineCommentFormat = QTextCharFormat()
+ singleLineCommentFormat.setForeground(Qt.darkGray)
+ self.highlightingRules.append(("#[^\n]*", singleLineCommentFormat))
+
+ classOrFnNameFormat = QTextCharFormat()
+ classOrFnNameFormat.setForeground(QColor(96, 106, 161))
+ self.highlightingRules.append(("(?<=\\bclass\\s|def\\s\\b)\\s*(\\w+)", classOrFnNameFormat))
+
+ quotationFormat = QTextCharFormat()
+ quotationFormat.setForeground(QColor(223, 17, 68))
+ self.highlightingRules.append(("'.*'|[\"]{1,3}.*[\"]{1,3}", quotationFormat))