diff options
Diffstat (limited to 'Lib/defconQt')
| -rw-r--r-- | Lib/defconQt/featureTextEditor.py | 37 | ||||
| -rw-r--r-- | Lib/defconQt/fontView.py | 55 | ||||
| -rw-r--r-- | Lib/defconQt/groupsView.py | 71 |
3 files changed, 129 insertions, 34 deletions
diff --git a/Lib/defconQt/featureTextEditor.py b/Lib/defconQt/featureTextEditor.py index 0786c03..9571229 100644 --- a/Lib/defconQt/featureTextEditor.py +++ b/Lib/defconQt/featureTextEditor.py @@ -148,7 +148,10 @@ class TextEditor(QPlainTextEdit): cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, len(self._indent)) if cursor.selectedText() == self._indent: indent += 1 - cursor.movePosition(QTextCursor.NoMove) + 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 @@ -210,6 +213,22 @@ class TextEditor(QPlainTextEdit): 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): def __init__(self, parent=None): super(Highlighter, self).__init__(parent) @@ -218,22 +237,6 @@ class Highlighter(QSyntaxHighlighter): keywordFormat.setForeground(QColor(30, 150, 220)) keywordFormat.setFontWeight(QFont.Bold) - 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"] - self.highlightingRules = [(QRegExp("%s%s%s" % ("(", "|".join(keywordPatterns), ")")), keywordFormat)] singleLineCommentFormat = QTextCharFormat() diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py index 6168ffd..64019f2 100644 --- a/Lib/defconQt/fontView.py +++ b/Lib/defconQt/fontView.py @@ -8,6 +8,9 @@ from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * +cannedDesign = [ + dict(type="cannedDesign", allowPseudoUnicode=True) +] glyphSortDescriptors = [ dict(type="alphabetical", allowPseudoUnicode=True), dict(type="category", allowPseudoUnicode=True), @@ -16,9 +19,6 @@ glyphSortDescriptors = [ dict(type="suffix", allowPseudoUnicode=True), dict(type="decompositionBase", allowPseudoUnicode=True) ] -cannedDesign = [ - dict(type="cannedDesign", allowPseudoUnicode=True) -] cellGridColor = QColor(130, 130, 130) cellHeaderBaseColor = QColor(230, 230, 230) @@ -37,26 +37,31 @@ class CharacterWidget(QWidget): super(CharacterWidget, self).__init__(parent) self.font = font - #self.glyphs = [font[k] for k in font.unicodeData.sortGlyphNames(font.keys(), glyphSortDescriptors)] - self.glyphs = [font[k] for k in font.unicodeData.sortGlyphNames(font.keys(), cannedDesign)] + self.glyphs = [] self.scrollArea = scrollArea self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.squareSize = squareSize self.columns = 10 self._selection = set() self.lastKey = -1 self.moveKey = -1 + self._maybeDragPosition = None self.setFocusPolicy(Qt.ClickFocus) def updateFont(self, font): self.font = font - self.glyphs = [font[k] for k in font.unicodeData.sortGlyphNames(font.keys(), cannedDesign)] + self.updateGlyphsFromFont() + + def updateGlyphsFromFont(self, descriptor=cannedDesign): + self.glyphs = [self.font[k] for k in self.font.unicodeData.sortGlyphNames(self.font.keys(), descriptor)] self.adjustSize() self.update() - def updateGlyphs(self): - self.glyphs = [self.font[k] for k in self.font.unicodeData.sortGlyphNames(self.font.keys(), cannedDesign)] + def setGlyphs(self, glyphs): + self.glyphs = glyphs + self._selection = set() self.adjustSize() self.update() @@ -76,7 +81,11 @@ class CharacterWidget(QWidget): def markSelection(self, color): for key in self._selection: glyph = self.glyphs[key] - glyph.lib["public.markColor"] = ",".join(str(c) for c in color.getRgbF()) + if color is None: + if "public.markColor" in glyph.lib: + del glyph.lib["public.markColor"] + else: + glyph.lib["public.markColor"] = ",".join(str(c) for c in color.getRgbF()) self.update() # TODO: eventually get rid of the signal @@ -101,6 +110,11 @@ class CharacterWidget(QWidget): self.computeCharacterSelected() self.update() event.accept() + elif event.key() == Qt.Key_D and event.modifiers() & Qt.ControlModifier: + self._selection = set() + self.computeCharacterSelected() + self.update() + event.accept() else: super(CharacterWidget, self).keyPressEvent(event) @@ -112,6 +126,10 @@ class CharacterWidget(QWidget): if modifiers & Qt.ShiftModifier and len(self._selection)==1: self.lastKey = self._selection.pop() self.moveKey = key + elif key in self._selection: + self._maybeDragPosition = event.pos() + event.accept() + return else: self.lastKey = key self.moveKey = self.lastKey @@ -126,6 +144,19 @@ class CharacterWidget(QWidget): def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: + if self._maybeDragPosition is not None: + if ((event.pos() - self._maybeDragPosition).manhattanLength() \ + < QApplication.startDragDistance()): return + # TODO: needs ordering or not? + glyphList = " ".join((self.glyphs[key].name for key in self._selection)) + drag = QDrag(self) + mimeData = QMimeData() + mimeData.setData("text/plain", glyphList) + drag.setMimeData(mimeData) + + dropAction = drag.exec_() + event.accept() + return key = (event.y() // self.squareSize) * self.columns + min(event.x() // self.squareSize, self.columns-1) if key > len(self.glyphs)-1: return self.moveKey = key @@ -138,6 +169,7 @@ class CharacterWidget(QWidget): def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: + self._maybeDragPosition = None lastKey = self.lastKey if self.lastKey < len(self.glyphs) else len(self.glyphs)-1 moveKey = self.moveKey if self.moveKey < len(self.glyphs) else len(self.glyphs)-1 if event.modifiers() & Qt.ControlModifier: @@ -292,6 +324,7 @@ class MainWindow(QMainWindow): self.scrollArea = QScrollArea(self) squareSize = 56 self.characterWidget = CharacterWidget(self.font, squareSize, self.scrollArea, self) + self.characterWidget.updateGlyphsFromFont() self.scrollArea.setWidget(self.characterWidget) # TODO: make shortcuts platform-independent @@ -312,6 +345,8 @@ class MainWindow(QMainWindow): markColorMenu = QMenu("Mark color", self) pixmap = QPixmap(24, 24) + none = markColorMenu.addAction("None", self.colorFill) + none.setData(None) red = markColorMenu.addAction("Red", self.colorFill) pixmap.fill(Qt.red) red.setIcon(QIcon(pixmap)) @@ -504,7 +539,7 @@ class MainWindow(QMainWindow): if ok and gName != '': self.font.newGlyph(gName) self.font[gName].width = 500 - self.characterWidget.updateGlyphs() + self.characterWidget.updateGlyphsFromFont() def about(self): QMessageBox.about(self, "About Me", diff --git a/Lib/defconQt/groupsView.py b/Lib/defconQt/groupsView.py index db4645f..a88d9eb 100644 --- a/Lib/defconQt/groupsView.py +++ b/Lib/defconQt/groupsView.py @@ -1,28 +1,64 @@ +from fontView import CharacterWidget from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * +class GroupCharacterWidget(CharacterWidget): + def __init__(self, font, squareSize=56, scrollArea=None, parent=None): + super(GroupCharacterWidget, self).__init__(font, squareSize, scrollArea, parent) + self.columns = 8 + self.scrollArea.setAcceptDrops(True) + self.scrollArea.dragEnterEvent = self.pipeDragEnterEvent + self.scrollArea.dropEvent = self.pipeDropEvent + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Delete: + self.parent().parent().parent().characterDeleteEvent(self._selection) + event.accept() + else: + super(GroupCharacterWidget, self).keyPressEvent(event) + + def pipeDragEnterEvent(self, event): + # TODO: the problem with text/plain is that any sort of text can get here. + # (It allows direct compatibility with featureTextEditor though.) + if (event.mimeData().hasFormat("text/plain")): + event.acceptProposedAction() + + def pipeDropEvent(self, event): + self.parent().parent().parent().characterDropEvent(event) + class GroupsWindow(QWidget): def __init__(self, font, parent=None): super(GroupsWindow, self).__init__(parent, Qt.Window) self.font = font - self.groups = sorted(font.groups.keys(), key=lambda t: t[0]) self.groupsList = QListWidget(self) - #self.groupsList.addItems(self.font.groups.keys()) - #self.groupsList.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed) + self.groupsList.setSelectionMode(QAbstractItemView.SingleSelection) + self.groupsList.setSortingEnabled(True) for groupName in self.font.groups.keys(): item = QListWidgetItem(groupName, self.groupsList) - #item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable) item.setFlags(item.flags() | Qt.ItemIsEditable) self.groupsList.itemChanged.connect(self._groupRenamed) - layout = QVBoxLayout(self) + self.scrollArea = QScrollArea(self) + self.characterWidget = GroupCharacterWidget(self.font, scrollArea=self.scrollArea, parent=self) + self.scrollArea.setWidget(self.characterWidget) + self.groupsList.currentItemChanged.connect(self._groupChanged) + + layout = QHBoxLayout(self) layout.addWidget(self.groupsList) + layout.addWidget(self.scrollArea) self.setLayout(layout) self.setWindowTitle("%s%s%s%s" % ("Groups window – ", self.font.info.familyName, " ", self.font.info.styleName)) + def _groupChanged(self): + currentGroup = self.groupsList.currentItem().text() + glyphs = [] + for gName in self.font.groups[currentGroup]: + glyphs.append(self.font[gName]) + self.characterWidget.setGlyphs(glyphs) + def _groupRenamed(self): cur = self.groupsList.currentItem() # XXX: perf? @@ -30,5 +66,26 @@ class GroupsWindow(QWidget): newKey = cur.text() self.font.groups[newKey] = self.font.groups[self.groups[index]] del self.font.groups[self.groups[index]] - self.groups[index] = newKey - #print(self.groupsList.currentItem().text())
\ No newline at end of file + #print(self.groupsList.currentItem().text()) + + def characterDeleteEvent(self, selection): + currentGroup = self.groupsList.currentItem().text() + currentGroupList = self.font.groups[currentGroup] + # relying on ordered group elements + # reverse to not change index of smaller elements + for key in sorted(selection, reverse=True): + del currentGroupList[key] + self.font.groups[currentGroup] = currentGroupList + self.characterWidget.update() + + def characterDropEvent(self, event): + currentGroup = self.groupsList.currentItem().text() + glyphNames = event.mimeData().text().split(" ") + for gName in glyphNames: + # Due to defcon limitations, we must fetch and update for the + # notification to pass through + currentGroupList = self.font.groups[currentGroup] + currentGroupList.append(gName) + self.font.groups[currentGroup] = currentGroupList + event.acceptProposedAction() + self._groupChanged()
\ No newline at end of file |
