aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrien Tétar2015-09-21 20:40:35 +0200
committerAdrien Tétar2015-09-21 20:40:35 +0200
commitf83831e31597e8c811f5b3b75cb98a0ff3590a8c (patch)
tree81665d9e00cf3752bd245b1c64a920b1781cc669
parent748bb567eef19dcd8e067934786950c5866dc580 (diff)
downloadtrufont-f83831e31597e8c811f5b3b75cb98a0ff3590a8c.tar.bz2
meta: refactorings and cleanups, fontView: partial rewrite, new AddGlyphsWindow, extract cells widget to a separate location, support CharacterSet objects fully
-rw-r--r--Lib/defconQt/__init__.py2
-rw-r--r--Lib/defconQt/__main__.py3
-rw-r--r--Lib/defconQt/featureTextEditor.py40
-rw-r--r--Lib/defconQt/fontView.py755
-rw-r--r--Lib/defconQt/glyphCollectionView.py381
-rw-r--r--Lib/defconQt/glyphView.py218
-rw-r--r--Lib/defconQt/groupsView.py91
-rw-r--r--Lib/defconQt/objects/__init__.py0
-rw-r--r--Lib/defconQt/objects/defcon.py22
-rw-r--r--Lib/defconQt/spaceCenter.py187
-rw-r--r--README.md2
-rw-r--r--setup.py5
12 files changed, 960 insertions, 746 deletions
diff --git a/Lib/defconQt/__init__.py b/Lib/defconQt/__init__.py
index 5f15410..9f7a875 100644
--- a/Lib/defconQt/__init__.py
+++ b/Lib/defconQt/__init__.py
@@ -1 +1 @@
-version = "0.1"
+version = "0.1.0"
diff --git a/Lib/defconQt/__main__.py b/Lib/defconQt/__main__.py
index babcc73..07f273c 100644
--- a/Lib/defconQt/__main__.py
+++ b/Lib/defconQt/__main__.py
@@ -19,8 +19,7 @@ representationFactories.registerAllFactories()
#with PyCallGraph(output=GraphvizOutput()):
app = QApplication(sys.argv)
# TODO: http://stackoverflow.com/a/21330349/2037879
-app.setWindowIcon(QIcon("resources/icon.png"))
+app.setWindowIcon(QIcon("defconQt/resources/icon.png"))
window = MainWindow(Font(ufoFile))
-window.resize(605, 430)
window.show()
sys.exit(app.exec_())
diff --git a/Lib/defconQt/featureTextEditor.py b/Lib/defconQt/featureTextEditor.py
index 1cff1b8..cadb9d5 100644
--- a/Lib/defconQt/featureTextEditor.py
+++ b/Lib/defconQt/featureTextEditor.py
@@ -17,12 +17,12 @@ class MainEditWindow(QMainWindow):
self.setCentralWidget(self.editor)
self.setWindowTitle("Font features", self.font)
-
+
def setWindowTitle(self, title, font):
- if font is not None: puts = "[*]%s%s%s%s%s" % (title, " – ", self.font.info.familyName, " ", self.font.info.styleName)
+ if font is not None: puts = "[*]%s – %s %s" % (title, self.font.info.familyName, self.font.info.styleName)
else: puts = "[*]%s" % title
super(MainEditWindow, self).setWindowTitle(puts)
-
+
def closeEvent(self, event):
if self.editor.document().isModified():
closeDialog = QMessageBox(QMessageBox.Question, "Me", "Save your changes?",
@@ -37,7 +37,7 @@ class MainEditWindow(QMainWindow):
event.accept()
else:
event.ignore()
-
+
def reload(self):
self.font.reloadFeatures()
self.editor.setPlainText(self.font.features.text)
@@ -93,7 +93,7 @@ class TextEditor(QPlainTextEdit):
def write(self, features):
features.text = self.toPlainText()
-
+
def lineNumberAreaPaintEvent(self, event):
painter = QPainter(self.lineNumbers)
painter.fillRect(event.rect(), QColor(230, 230, 230))
@@ -103,23 +103,23 @@ class TextEditor(QPlainTextEdit):
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,
+ 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())
@@ -129,25 +129,25 @@ class TextEditor(QPlainTextEdit):
# 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.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)
@@ -247,7 +247,7 @@ class Highlighter(QSyntaxHighlighter):
keywordFormat.setForeground(QColor(30, 150, 220))
keywordFormat.setFontWeight(QFont.Bold)
- self.highlightingRules = [(QRegExp("%s%s%s" % ("(", "|".join(keywordPatterns), ")")), keywordFormat)]
+ self.highlightingRules = [(QRegExp("(%s)" % ("|".join(keywordPatterns))), keywordFormat)]
singleLineCommentFormat = QTextCharFormat()
singleLineCommentFormat.setForeground(Qt.darkGray)
@@ -270,13 +270,3 @@ class Highlighter(QSyntaxHighlighter):
index = expression.indexIn(text, index + length)
self.setCurrentBlockState(0)
-
-if __name__ == '__main__':
-
- import sys
-
- app = QApplication(sys.argv)
- window = MainWindow()
- window.resize(640, 512)
- window.show()
- sys.exit(app.exec_())
diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py
index cf08fe9..ba773c3 100644
--- a/Lib/defconQt/fontView.py
+++ b/Lib/defconQt/fontView.py
@@ -1,55 +1,149 @@
-import math
-import os
-import unicodedata
-
from defcon import Font
from defconQt.featureTextEditor import MainEditWindow
from defconQt.fontInfo import TabDialog
+from defconQt.glyphCollectionView import GlyphCollectionWidget
from defconQt.glyphView import MainGfxWindow
from defconQt.groupsView import GroupsWindow
+from defconQt.objects.defcon import CharacterSet
from defconQt.spaceCenter import MainSpaceWindow
+from fontTools.agl import AGL2UV
# TODO: remove globs when things start to stabilize
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
+import os
+import unicodedata
cannedDesign = [
dict(type="cannedDesign", allowPseudoUnicode=True)
]
sortItems = ["alphabetical", "category", "unicode", "script", "suffix",
"decompositionBase", "weightedSuffix", "ligature"]
+latin1 = CharacterSet(
+["space","exclam","quotesingle","quotedbl","numbersign","dollar",
+"percent","ampersand","parenleft","parenright","asterisk","plus","comma",
+"hyphen","period","slash","zero","one","two","three","four","five",
+"six","seven","eight","nine","colon","semicolon","less","equal",
+"greater","question","at","A","B","C","D","E","F","G","H","I","J",
+"K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
+"bracketleft","backslash","bracketright","asciicircum","underscore","grave",
+"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t",
+"u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown",
+"cent","sterling","currency","yen","brokenbar","section","dieresis","copyright",
+"ordfeminine","guillemotleft","logicalnot","registered","macron","degree",
+"plusminus","twosuperior","threesuperior","acute","mu","paragraph",
+"periodcentered","cedilla","onesuperior","ordmasculine","guillemotright",
+"onequarter","onehalf","threequarters","questiondown","Agrave","Aacute",
+"Acircumflex","Atilde","Adieresis","Aring","AE","Ccedilla","Egrave","Eacute",
+"Ecircumflex","Edieresis","Igrave","Iacute","Icircumflex","Idieresis","Eth",
+"Ntilde","Ograve","Oacute","Ocircumflex","Otilde","Odieresis","multiply",
+"Oslash","Ugrave","Uacute","Ucircumflex","Udieresis","Yacute","Thorn",
+"germandbls","agrave","aacute","acircumflex","atilde","adieresis","aring","ae",
+"ccedilla","egrave","eacute","ecircumflex","edieresis","igrave","iacute",
+"icircumflex","idieresis","eth","ntilde","ograve","oacute","ocircumflex",
+"otilde","odieresis","divide","oslash","ugrave","uacute","ucircumflex",
+"udieresis","yacute","thorn","ydieresis","dotlessi","circumflex","caron",
+"breve","dotaccent","ring","ogonek","tilde","hungarumlaut","quoteleft",
+"quoteright","minus"], "Latin-1")
+
+# TODO: implement Frederik's Glyph Construction Builder
+class AddGlyphDialog(QDialog):
+ def __init__(self, currentGlyphs=None, parent=None):
+ super(AddGlyphDialog, self).__init__(parent)
+ self.setWindowModality(Qt.WindowModal)
+ self.setWindowTitle("Add glyphs…")
+ self.currentGlyphs = currentGlyphs
+ self.currentGlyphNames = [glyph.name for glyph in currentGlyphs]
+
+ layout = QGridLayout(self)
+ self.importCharDrop = QComboBox(self)
+ self.importCharDrop.addItem("Import glyphnames…")
+ self.importCharDrop.addItem("Latin-1", latin1)
+ self.importCharDrop.currentIndexChanged[int].connect(self.importCharacters)
+ self.addGlyphEdit = QPlainTextEdit(self)
+ self.addGlyphEdit.setFocus(True)
+
+ self.sortFontBox = QCheckBox("Sort font", self)
+ self.overwriteBox = QCheckBox("Override", self)
+ buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ buttonBox.accepted.connect(self.accept)
+ buttonBox.rejected.connect(self.reject)
-cellGridColor = QColor(130, 130, 130)
-cellHeaderBaseColor = QColor(230, 230, 230)
-cellHeaderLineColor = QColor(220, 220, 220)
-cellHeaderHighlightLineColor = QColor(240, 240, 240)
-cellSelectionColor = QColor.fromRgbF(.2, .3, .7, .15)
-
-GlyphCellBufferHeight = .2
-GlyphCellHeaderHeight = 14
+ l = 0
+ layout.addWidget(self.importCharDrop, l, 3)
+ l += 1
+ layout.addWidget(self.addGlyphEdit, l, 0, 1, 4)
+ l += 1
+ layout.addWidget(self.sortFontBox, l, 0)
+ layout.addWidget(self.overwriteBox, l, 1)
+ layout.addWidget(buttonBox, l, 3)
+ self.setLayout(layout)
-class SortingWindow(QDialog):
- def __init__(self, parent=None):
- super(SortingWindow, self).__init__(parent)
+ @staticmethod
+ def getNewGlyphNames(parent=None, currentGlyphs=None):
+ dialog = AddGlyphDialog(currentGlyphs, parent)
+ result = dialog.exec_()
+ sortFont = False
+ newGlyphNames = []
+ for name in dialog.addGlyphEdit.toPlainText().split():
+ if name not in dialog.currentGlyphNames:
+ newGlyphNames.append(name)
+ if dialog.sortFontBox.isChecked():
+ # XXX: if we get here with previous sort being by character set,
+ # should it just stick?
+ sortFont = True
+ return (newGlyphNames, sortFont, result)
+
+ def importCharacters(self, index):
+ if index == 0: return
+ charset = self.importCharDrop.currentData()
+ editorNames = self.addGlyphEdit.toPlainText().split()
+ currentNames = set(self.currentGlyphNames) ^ set(editorNames)
+ changed = False
+ for name in charset.glyphNames:
+ if name not in currentNames:
+ changed = True
+ editorNames.append(name)
+ if changed:
+ self.addGlyphEdit.setPlainText(" ".join(editorNames))
+ cursor = self.addGlyphEdit.textCursor()
+ cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
+ self.addGlyphEdit.setTextCursor(cursor)
+ self.importCharDrop.setCurrentIndex(0)
+ self.addGlyphEdit.setFocus(True)
+
+class SortDialog(QDialog):
+ def __init__(self, desc=None, parent=None):
+ super(SortDialog, self).__init__(parent)
self.setWindowModality(Qt.WindowModal)
self.setWindowTitle("Sort…")
-
+
self.smartSortBox = QRadioButton("Smart sort", self)
self.characterSetBox = QRadioButton("Character set", self)
- self.characterSetBox.setEnabled(False)
+ self.characterSetBox.toggled.connect(self.characterSetToggle)
+ self.characterSetDrop = QComboBox(self)
+ self.characterSetDrop.setEnabled(False)
+ # XXX: fetch from settings
+ self.characterSetDrop.insertItem(0, "Latin 1")
self.customSortBox = QRadioButton("Custom…", self)
self.customSortBox.toggled.connect(self.customSortToggle)
-
+
self.customSortGroup = QGroupBox(parent=self)
- desc = self.parent().characterWidget.sortDescriptor
- if desc[0]["type"] == "cannedDesign":
+ self.customSortGroup.setEnabled(False)
+ descriptorsCount = 6
+ if desc is None:
+ # sort fetched from public.glyphOrder. no-op
+ pass
+ elif isinstance(desc, CharacterSet):
+ self.characterSetBox.setChecked(True)
+ self.characterSetDrop.setEnabled(True)
+ # TODO: match cset name when QSettings support lands
+ elif desc[0]["type"] == "cannedDesign":
self.smartSortBox.setChecked(True)
- self.customSortGroup.setEnabled(False)
- descriptorsCount = 6
else:
self.customSortBox.setChecked(True)
+ self.customSortGroup.setEnabled(True)
descriptorsCount = len(desc)
- #elif desc ==
self.customDescriptors = [[] for i in range(descriptorsCount)]
self.customSortLayout = QGridLayout()
for i, line in enumerate(self.customDescriptors):
@@ -81,19 +175,20 @@ class SortingWindow(QDialog):
btn.setText("−")
btn.pressed.connect(self._deleteRow)
self.customSortGroup.setLayout(self.customSortLayout)
-
+
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
-
+
layout = QVBoxLayout(self)
layout.addWidget(self.smartSortBox)
layout.addWidget(self.characterSetBox)
+ layout.addWidget(self.characterSetDrop)
layout.addWidget(self.customSortBox)
layout.addWidget(self.customSortGroup)
layout.addWidget(buttonBox)
self.setLayout(layout)
-
+
def _addRow(self):
i = len(self.customDescriptors)
line = []
@@ -113,8 +208,7 @@ class SortingWindow(QDialog):
self.customSortLayout.addWidget(line[2], i, 2)
self.customSortLayout.addWidget(line[3], i, 3)
if i == 7: self.sender().setEnabled(False)
-
-
+
def _deleteRow(self):
rel = self.sender().property("index")
desc = self.customDescriptors
@@ -127,356 +221,53 @@ class SortingWindow(QDialog):
del self.customDescriptors[-1]
self.addLineButton.setEnabled(True)
self.adjustSize()
-
+
def indexFromItemName(self, name):
for index, item in enumerate(sortItems):
if name == item: return index
print("Unknown descriptor name: %s", name)
return 0
-
- def accept(self):
- if self.smartSortBox.isChecked():
- descriptors = cannedDesign
- elif self.customSortBox.isChecked():
+
+ @staticmethod
+ def getDescriptor(parent=None, sortDescriptor=None):
+ dialog = SortDialog(sortDescriptor, parent)
+ result = dialog.exec_()
+ if dialog.characterSetBox.isChecked():
+ # TODO: dispatch csets when QSettings support lands
+ ret = latin1
+ elif dialog.customSortBox.isChecked():
descriptors = []
- for line in self.customDescriptors:
+ for line in dialog.customDescriptors:
descriptors.append(dict(type=line[0].currentText(), ascending=line[1].isChecked(),
allowPseudoUnicode=line[2].isChecked()))
- self.parent().characterWidget.updateGlyphsFromFont(descriptors)
- super(SortingWindow, self).accept()
-
+ ret = descriptors
+ else:
+ ret = cannedDesign
+ return (ret, result)
+
+ def characterSetToggle(self):
+ checkBox = self.sender()
+ self.characterSetDrop.setEnabled(checkBox.isChecked())
+
def customSortToggle(self):
checkBox = self.sender()
self.customSortGroup.setEnabled(checkBox.isChecked())
-class CharacterWidget(QWidget):
- characterSelected = pyqtSignal(int, str)
- glyphOpened = pyqtSignal(str)
-
- def __init__(self, font, squareSize=56, scrollArea=None, parent=None):
- super(CharacterWidget, self).__init__(parent)
-
- self.font = font
- 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.sortDescriptor = cannedDesign
-
- self._maybeDragPosition = None
- self.setFocusPolicy(Qt.ClickFocus)
-
- def updateFont(self, font):
- self.font = font
- self.updateGlyphsFromFont()
-
- def updateGlyphsFromFont(self, descriptor=None):
- if descriptor is not None: self.sortDescriptor = descriptor
- self.glyphs = [self.font[k] for k in self.font.unicodeData.sortGlyphNames(self.font.keys(), self.sortDescriptor)]
- self._selection = set()
- self.adjustSize()
- self.update()
-
- def setGlyphs(self, glyphs):
- self.glyphs = glyphs
- self._selection = set()
- self.adjustSize()
- self.update()
-
- def _sizeEvent(self, width, squareSize=None):
- if self.scrollArea is not None: sw = self.scrollArea.verticalScrollBar().width() + self.scrollArea.contentsMargins().right()
- else: sw = 0
- if squareSize is not None: self.squareSize = squareSize
- columns = (width - sw) // self.squareSize
- if not columns > 0: return
- self.columns = columns
- self.adjustSize()
-
- def sizeHint(self):
- # Calculate sizeHint with max(height, scrollArea.height()) because if scrollArea is
- # bigger than widget height after an update, we risk leaving old painted content on screen
- return QSize(self.columns * self.squareSize,
- max(math.ceil(len(self.glyphs) / self.columns) * self.squareSize, self.scrollArea.height()))
-
- def markSelection(self, color):
- for key in self._selection:
- glyph = self.glyphs[key]
- 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
- def computeCharacterSelected(self):
- lKey, mKey = self.lastKey, self.moveKey
- mKey = self.moveKey if self.moveKey < len(self.glyphs) else len(self.glyphs)-1
- lKey = self.lastKey if self.lastKey < len(self.glyphs) else len(self.glyphs)-1
- if lKey == -1:
- elements = set()
- elif lKey > mKey:
- elements = set(range(mKey, lKey+1))
- else:
- elements = set(range(lKey, mKey+1))
- elements ^= self._selection
- if len(elements)>1: self.characterSelected.emit(len(elements), "")
- elif len(elements)>0: self.characterSelected.emit(1, self.glyphs[elements.pop()].name)
- else: self.characterSelected.emit(0, "")
-
- def keyPressEvent(self, event):
- if event.key() == Qt.Key_A and event.modifiers() & Qt.ControlModifier:
- self._selection = set(range(len(self.glyphs)))
- 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)
-
- def mousePressEvent(self, event):
- if event.button() == Qt.LeftButton:
- key = (event.y() // self.squareSize) * self.columns + event.x() // self.squareSize
- if key > len(self.glyphs)-1: return
- modifiers = event.modifiers()
- if modifiers & Qt.ShiftModifier and len(self._selection) == 1:
- self.lastKey = self._selection.pop()
- self.moveKey = key
- elif modifiers & Qt.ControlModifier:
- self.lastKey = key
- self.moveKey = self.lastKey
- elif key in self._selection and not modifiers & Qt.ShiftModifier:
- self._maybeDragPosition = event.pos()
- event.accept()
- return
- else:
- self._selection = set()
- self.lastKey = key
- self.moveKey = self.lastKey
-
- self.computeCharacterSelected()
- event.accept()
- self.update()
- else:
- super(CharacterWidget, self).mousePressEvent(event)
-
- 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_()
- self._maybeDragPosition = None
- 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
-
- self.computeCharacterSelected()
- event.accept()
- self.update()
- else:
- super(CharacterWidget, self).mouseMoveEvent(event)
-
- def mouseReleaseEvent(self, event):
- if event.button() == Qt.LeftButton:
- self._maybeDragPosition = None
- if self.lastKey == -1:
- if self._maybeDragPosition is None:
- key = (event.y() // self.squareSize) * self.columns + event.x() // self.squareSize
- if key > len(self.glyphs)-1: return
- self._selection = {key}
- self.computeCharacterSelected()
- else:
- 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:
- if moveKey > lastKey:
- self._selection ^= set(range(lastKey, moveKey+1))
- else:
- self._selection ^= set(range(moveKey, lastKey+1))
- else:
- if moveKey > lastKey:
- self._selection = set(range(lastKey, moveKey+1))
- else:
- self._selection = set(range(moveKey, lastKey+1))
- self.lastKey = -1
- self.moveKey = -1
- event.accept()
- self.update()
- else:
- super(CharacterWidget, self).mouseReleaseEvent(event)
-
- def mouseDoubleClickEvent(self, event):
- if event.button() == Qt.LeftButton:
- key = (event.y() // self.squareSize) * self.columns + event.x() // self.squareSize
- if key > len(self.glyphs)-1: event.ignore(); return
- self._selection -= {key}
- self.lastKey = key
- self.moveKey = self.lastKey
- event.accept()
- self.glyphOpened.emit(self.glyphs[key].name)
- else:
- super(CharacterWidget, self).mousePressEvent(event)
-
- # TODO: see if more of this process can be delegated to a factory
- def paintEvent(self, event):
- painter = QPainter(self)
- painter.setRenderHint(QPainter.Antialiasing)
-
- redrawRect = event.rect()
- beginRow = redrawRect.top() // self.squareSize
- endRow = redrawRect.bottom() // self.squareSize
- beginColumn = redrawRect.left() // self.squareSize
- endColumn = redrawRect.right() // self.squareSize
-
- # painter.setPen(cellGridColor)
- # painter.drawLine(redrawRect.left(), redrawRect.top(), redrawRect.left()+self.squareSize \
- # *min(len(self.glyphs), self.columns), redrawRect.top()+self.squareSize*(math.ceil(len(self.glyphs)/self.columns)))
- # painter.drawLine(0, 0, redrawRect.right(), 0)
-
- # selection code
- if self.moveKey != -1:
- if self.moveKey > self.lastKey:
- curSelection = set(range(self.lastKey, self.moveKey+1))
- else:
- curSelection = set(range(self.moveKey, self.lastKey+1))
- elif self.lastKey != -1: # XXX: necessary?
- curSelection = {self.lastKey}
- else:
- curSelection = set()
- curSelection ^= self._selection
-
- gradient = QLinearGradient(0, 0, 0, GlyphCellHeaderHeight)
- gradient.setColorAt(0.0, cellHeaderBaseColor)
- gradient.setColorAt(1.0, cellHeaderLineColor)
- dirtyGradient = QLinearGradient(0, 0, 0, GlyphCellHeaderHeight)
- dirtyGradient.setColorAt(0.0, cellHeaderBaseColor.darker(125))
- dirtyGradient.setColorAt(1.0, cellHeaderLineColor.darker(125))
- #markGradient = QRadialGradient(self.squareSize/2, GlyphCellHeaderHeight/2,
- # self.squareSize-GlyphCellHeaderHeight, self.squareSize/2, self.squareSize)
- markGradient = QLinearGradient(0, 0, 0, self.squareSize-GlyphCellHeaderHeight)
- headerFont = QFont()
- headerFont.setFamily('Lucida Sans Unicode')
- headerFont.insertSubstitution('Lucida Sans Unicode', 'Luxi Sans')
- headerFont.setPointSize(8)
- metrics = QFontMetrics(headerFont)
-
- for row in range(beginRow, endRow + 1):
- for column in range(beginColumn, endColumn + 1):
- key = row * self.columns + column
- if key > len(self.glyphs)-1: break
-
- painter.save()
- painter.translate(column * self.squareSize, row * self.squareSize)
- # background
- painter.fillRect(0, 0, self.squareSize, self.squareSize, Qt.white)
- glyph = self.glyphs[key]
- if "public.markColor" in glyph.lib:
- colorStr = glyph.lib["public.markColor"].split(",")
- if len(colorStr) == 4:
- comp = []
- for c in colorStr:
- comp.append(float(c.strip()))
- markColor = QColor.fromRgbF(*comp)
- markGradient.setColorAt(1.0, markColor)
- markGradient.setColorAt(0.0, markColor.lighter(125))
- painter.fillRect(0, GlyphCellHeaderHeight, self.squareSize,
- self.squareSize - GlyphCellHeaderHeight, QBrush(markGradient))
-
- # header gradient
- if glyph.dirty: col = dirtyGradient
- else: col = gradient
- painter.fillRect(0, 0, self.squareSize,
- GlyphCellHeaderHeight, QBrush(col))
- # header lines
- if glyph.dirty: col = cellHeaderHighlightLineColor.darker(110)
- else: col = cellHeaderHighlightLineColor
- painter.setPen(col)
- minOffset = painter.pen().width()
- painter.setRenderHint(QPainter.Antialiasing, False)
- painter.drawLine(0, 0, 0, GlyphCellHeaderHeight - 1)
- painter.drawLine(self.squareSize - 2, 0, self.squareSize - 2, GlyphCellHeaderHeight -1)
- painter.setPen(QColor(170, 170, 170))
- painter.drawLine(0, GlyphCellHeaderHeight, self.squareSize, GlyphCellHeaderHeight)
- painter.setRenderHint(QPainter.Antialiasing)
- # header text
- painter.setFont(headerFont)
- painter.setPen(QColor(80, 80, 80))
- name = metrics.elidedText(self.glyphs[key].name, Qt.ElideRight, self.squareSize - 2)
- painter.drawText(1, 0, self.squareSize - 2, GlyphCellHeaderHeight - minOffset,
- Qt.TextSingleLine | Qt.AlignCenter, name)
- painter.restore()
-
- painter.setPen(cellGridColor)
- rightEdgeX = column * self.squareSize + self.squareSize
- bottomEdgeY = row * self.squareSize + self.squareSize
- painter.drawLine(rightEdgeX, row * self.squareSize + 1, rightEdgeX, bottomEdgeY)
- painter.drawLine(rightEdgeX, bottomEdgeY, column * self.squareSize + 1, bottomEdgeY)
-
- # selection code
- painter.setRenderHint(QPainter.Antialiasing, False)
- if key in curSelection:
- painter.fillRect(column * self.squareSize + 1,
- row * self.squareSize + 1, self.squareSize - 3,
- self.squareSize - 3, cellSelectionColor)
- painter.setRenderHint(QPainter.Antialiasing)
-
- glyph = self.glyphs[key].getRepresentation("defconQt.QPainterPath")
- if self.font.info.unitsPerEm is None: break
- if not self.font.info.unitsPerEm > 0: self.font.info.unitsPerEm = 1000
- factor = (self.squareSize-GlyphCellHeaderHeight)/(self.font.info.unitsPerEm*(1+2*GlyphCellBufferHeight))
- x_offset = (self.squareSize-self.glyphs[key].width*factor)/2
- # If the glyph overflows horizontally we need to adjust the scaling factor
- if x_offset < 0:
- factor *= 1+2*x_offset/(self.glyphs[key].width*factor)
- x_offset = 0
- # TODO: the * 1.8 below is somewhat artificial
- y_offset = self.font.info.descender*factor * 1.8
- painter.save()
- painter.setClipRect(column * self.squareSize, row * self.squareSize+GlyphCellHeaderHeight,
- self.squareSize, self.squareSize-GlyphCellHeaderHeight)
- painter.translate(column * self.squareSize + x_offset, row * self.squareSize + self.squareSize + y_offset)
- painter.scale(factor, -factor)
- painter.fillPath(glyph, Qt.black)
- painter.restore()
-
class MainWindow(QMainWindow):
- def __init__(self, font=Font()):
+ def __init__(self, font):
super(MainWindow, self).__init__()
-
- self.font = font
- self.font.addObserver(self, "_fontChanged", "Font.Changed")
- # TODO: have the scrollarea be part of the widget itself?
- # or better yet, switch to QGraphicsScene
- self.scrollArea = QScrollArea(self)
squareSize = 56
- self.characterWidget = CharacterWidget(self.font, squareSize, self.scrollArea, self)
- self.characterWidget.updateGlyphsFromFont()
- self.characterWidget.setFocus()
- self.scrollArea.setWidget(self.characterWidget)
+ self.collectionWidget = GlyphCollectionWidget(self)
+ self._font = None
+ self._sortDescriptor = None
+ self.font = font
+ self.collectionWidget.characterSelectedCallback = self._selectionChanged
+ self.collectionWidget.doubleClickCallback = self._glyphOpened
+ self.collectionWidget.setFocus()
+ menuBar = self.menuBar()
# TODO: make shortcuts platform-independent
fileMenu = QMenu("&File", self)
- self.menuBar().addMenu(fileMenu)
-
fileMenu.addAction("&New…", self.newFile, QKeySequence.New)
fileMenu.addAction("&Open…", self.openFile, QKeySequence.Open)
# TODO: add functionality
@@ -485,47 +276,48 @@ class MainWindow(QMainWindow):
fileMenu.addAction("&Save", self.saveFile, QKeySequence.Save)
fileMenu.addAction("Save &As…", self.saveFileAs, QKeySequence.SaveAs)
fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
+ menuBar.addMenu(fileMenu)
selectionMenu = QMenu("&Selection", self)
- self.menuBar().addMenu(selectionMenu)
-
markColorMenu = QMenu("Mark color", self)
pixmap = QPixmap(24, 24)
- none = markColorMenu.addAction("None", self.colorFill)
+ none = markColorMenu.addAction("None", self.markColor)
none.setData(None)
- red = markColorMenu.addAction("Red", self.colorFill)
+ red = markColorMenu.addAction("Red", self.markColor)
pixmap.fill(Qt.red)
red.setIcon(QIcon(pixmap))
red.setData(QColor(Qt.red))
- yellow = markColorMenu.addAction("Yellow", self.colorFill)
+ yellow = markColorMenu.addAction("Yellow", self.markColor)
pixmap.fill(Qt.yellow)
yellow.setIcon(QIcon(pixmap))
yellow.setData(QColor(Qt.yellow))
- green = markColorMenu.addAction("Green", self.colorFill)
+ green = markColorMenu.addAction("Green", self.markColor)
pixmap.fill(Qt.green)
green.setIcon(QIcon(pixmap))
green.setData(QColor(Qt.green))
selectionMenu.addMenu(markColorMenu)
- selectionMenu.addSeparator()
- selectionMenu.addAction("Sort…", self.sortCharacters)
+ menuBar.addMenu(selectionMenu)
fontMenu = QMenu("&Font", self)
- self.menuBar().addMenu(fontMenu)
-
- # TODO: work out sensible shortcuts
- fontMenu.addAction("Font &info", self.fontInfo, "Ctrl+I")
- fontMenu.addAction("Font &features", self.fontFeatures, "Ctrl+F")
+ # TODO: work out sensible shortcuts and make sure they're cross-platform
+ # ready - consider extracting them into separate file?
fontMenu.addAction("&Add glyph", self.addGlyph, "Ctrl+U")
+ fontMenu.addAction("Font &info", self.fontInfo, "Ctrl+M")
+ fontMenu.addAction("Font &features", self.fontFeatures, "Ctrl+F")
fontMenu.addSeparator()
- fontMenu.addAction("&Space center", self.spaceCenter, "Ctrl+Y")
- fontMenu.addAction("&Groups window", self.fontGroups, "Ctrl+G")
+ fontMenu.addAction("Sort…", self.sortCharacters)
+ menuBar.addMenu(fontMenu)
- helpMenu = QMenu("&Help", self)
- self.menuBar().addMenu(helpMenu)
+ windowMenu = QMenu("&Windows", self)
+ windowMenu.addAction("&Space center", self.spaceCenter, "Ctrl+Y")
+ windowMenu.addAction("&Groups window", self.fontGroups, "Ctrl+G")
+ menuBar.addMenu(windowMenu)
+ helpMenu = QMenu("&Help", self)
helpMenu.addAction("&About", self.about)
helpMenu.addAction("About &Qt", QApplication.instance().aboutQt)
-
+ menuBar.addMenu(helpMenu)
+
self.sqSizeSlider = QSlider(Qt.Horizontal, self)
self.sqSizeSlider.setMinimum(36)
self.sqSizeSlider.setMaximum(96)
@@ -533,15 +325,13 @@ class MainWindow(QMainWindow):
self.sqSizeSlider.setValue(squareSize)
self.sqSizeSlider.valueChanged.connect(self._squareSizeChanged)
self.selectionLabel = QLabel(self)
- self.statusBar().addPermanentWidget(self.sqSizeSlider)
- self.statusBar().addWidget(self.selectionLabel)
+ statusBar = self.statusBar()
+ statusBar.addPermanentWidget(self.sqSizeSlider)
+ statusBar.addWidget(self.selectionLabel)
- self.setCentralWidget(self.scrollArea)
- self.characterWidget.characterSelected.connect(self._selectionChanged)
- self.characterWidget.glyphOpened.connect(self._glyphOpened)
- self.setWindowTitle()
- # TODO: dump the hardcoded path
- #self.setWindowIcon(QIcon("C:\\Users\\Adrien\\Downloads\\defconQt\\Lib\\defconQt\\resources\\icon.png"))
+ self.setCentralWidget(self.collectionWidget.scrollArea())
+ self.resize(605, 430)
+ self.setWindowTitle() # TODO: maybe clean this up
def newFile(self):
ok = self.maybeSaveBeforeExit()
@@ -553,28 +343,36 @@ class MainWindow(QMainWindow):
self.font.info.capHeight = 750
self.font.info.xHeight = 500
self.setWindowTitle("Untitled.ufo")
- self.characterWidget.updateFont(self.font)
+ self.sortDescriptor = latin1
def openFile(self, path=None):
if not path:
- path, _ = QFileDialog.getOpenFileName(self, "Open File", '',
+ path, ok = QFileDialog.getOpenFileName(self, "Open File", '',
"UFO Fonts (metainfo.plist)")
-
+ if not ok: return
if path:
# TODO: error handling
path = os.path.dirname(path)
+ # TODO: I note that a change of self.font often goes with setWindowTitle().
+ # Be more DRY.
self.font = Font(path)
- self.characterWidget.updateFont(self.font)
self.setWindowTitle()
def saveFile(self, path=None):
if path is None and self.font.path is None:
self.saveFileAs()
else:
+ glyphs = self.collectionWidget.glyphs
+ # TODO: save sortDescriptor somewhere in lib as well
+ glyphNames = []
+ for glyph in glyphs:
+ glyphNames.append(glyph.name)
+ self.font.lib["public.glyphOrder"] = glyphNames
self.font.save(path=path)
-# self.font.dirty = False
+ self.font.dirty = False
+ for glyph in self.font:
+ glyph.dirty = False
self.setWindowModified(False)
-# self.font.path = path # done by defcon
def saveFileAs(self):
path, ok = QFileDialog.getSaveFileName(self, "Save File", '',
@@ -583,20 +381,21 @@ class MainWindow(QMainWindow):
self.saveFile(path)
self.setWindowTitle()
#return ok
-
+
def close(self):
self.font.removeObserver(self, "Font.Changed")
QApplication.instance().quit()
-
+
def closeEvent(self, event):
ok = self.maybeSaveBeforeExit()
if not ok: event.ignore()
else: event.accept()
-
+
def maybeSaveBeforeExit(self):
if self.font.dirty:
title = "Me"
if self.font.path is not None:
+ # TODO: maybe cache this font name in the Font
currentFont = os.path.basename(self.font.path.rstrip(os.sep))
else:
currentFont = "Untitled.ufo"
@@ -613,42 +412,110 @@ class MainWindow(QMainWindow):
return True
return False
return True
-
- def colorFill(self):
- action = self.sender()
- self.characterWidget.markSelection(action.data())
-
+
+ def _get_font(self):
+ return self._font
+
+ # TODO: consider that user may want to change font without sortDescriptor
+ # be calculated and set magically (and therefore, arbitrarily)
+ # In that case is it reasonable to just leave self._font?
+ def _set_font(self, font):
+ if self._font is not None:
+ self._font.removeObserver(self, "Font.Changed")
+ self._font = font
+ self._font.addObserver(self, "_fontChanged", "Font.Changed")
+ if "public.glyphOrder" in self._font.lib:
+ self.sortDescriptor = CharacterSet(
+ self._font.lib["public.glyphOrder"])
+ else:
+ # TODO: cannedDesign or carry sortDescriptor from previous font?
+ self.sortDescriptor = cannedDesign
+
+ font = property(_get_font, _set_font, doc="The fontView font. Subscribes \
+ to the new font, updates the sorting order and cells widget when set.")
+
+ def _get_sortDescriptor(self):
+ return self._sortDescriptor
+
+ def _set_sortDescriptor(self, desc):
+ if isinstance(desc, CharacterSet):
+ cnt = 0
+ glyphs = []
+ for glyphName in desc.glyphNames:
+ if not glyphName in self._font:
+ # create a template glyph
+ self.newStandardGlyph(glyphName)
+ self._font[glyphName].template = True
+ else:
+ cnt += 1
+ glyphs.append(self._font[glyphName])
+ if cnt < len(self._font):
+ # somehow some glyphs in the font are not present in the glyph
+ # order, loop again to add these at the end
+ for glyph in self._font:
+ if not glyph in glyphs:
+ glyphs.append(glyph)
+ else:
+ glyphs = [self._font[k] for k in self._font.unicodeData
+ .sortGlyphNames(self._font.keys(), desc)]
+ self.collectionWidget.glyphs = glyphs
+ self._sortDescriptor = desc
+
+ sortDescriptor = property(_get_sortDescriptor, _set_sortDescriptor,
+ doc="The sortDescriptor. Takes glyphs from the font and sorts them \
+ when set.")
+
+ def markColor(self):
+ color = self.sender().data()
+ glyphs = self.collectionWidget.glyphs
+ for key in self.collectionWidget.selection:
+ glyph = glyphs[key]
+ 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())
+
+ def newStandardGlyph(self, name):
+ self.font.newGlyph(name)
+ self.font[name].width = 500
+ # TODO: we should not force-add unicode, also list ought to be
+ # changeable from AGL2UV
+ if name in AGL2UV: self.font[name].unicode = AGL2UV[name]
+
def _fontChanged(self, notification):
- self.characterWidget.update()
+ self.collectionWidget.update()
self.setWindowModified(True)
- def _glyphOpened(self, name):
- glyphViewWindow = MainGfxWindow(self.font, self.font[name], self)
+ def _glyphOpened(self, glyph):
+ glyphViewWindow = MainGfxWindow(glyph, self)
glyphViewWindow.show()
-
- def _selectionChanged(self, count, glyph):
- if count == 0: self.selectionLabel.setText("")
- else: self.selectionLabel.setText("%s%s%s%d %s" % (glyph, " " if count <= 1 else "", "(", count, "selected)"))
+
+ def _selectionChanged(self, selection):
+ if selection is not None:
+ if isinstance(selection, str):
+ count = 1
+ text = "%s " % selection
+ else:
+ count = selection
+ text = ""
+ if not count == 0:
+ text = "%s(%d selected)" % (text, count)
+ else: text = ""
+ self.selectionLabel.setText(text)
def _squareSizeChanged(self):
val = self.sqSizeSlider.value()
- self.characterWidget._sizeEvent(self.width(), val)
+ self.collectionWidget._sizeEvent(self.width(), val)
QToolTip.showText(QCursor.pos(), str(val), self)
def resizeEvent(self, event):
- if self.isVisible(): self.characterWidget._sizeEvent(event.size().width())
+ if self.isVisible(): self.collectionWidget._sizeEvent(event.size().width())
super(MainWindow, self).resizeEvent(event)
-
+
def setWindowTitle(self, title=None):
if title is None: title = os.path.basename(self.font.path.rstrip(os.sep))
super(MainWindow, self).setWindowTitle("[*]{}".format(title))
-
- def sortCharacters(self):
- if not (hasattr(self, 'sortingWindow') and self.sortingWindow.isVisible()):
- self.sortingWindow = SortingWindow(self)
- self.sortingWindow.show()
- else:
- self.sortingWindow.raise_()
def fontInfo(self):
# If a window is already opened, bring it to the front, else spawn one.
@@ -656,20 +523,20 @@ class MainWindow(QMainWindow):
# it seems we're just leaking memory after each close... (both raise_ and
# show allocate memory instead of using the hidden widget it seems)
if not (hasattr(self, 'fontInfoWindow') and self.fontInfoWindow.isVisible()):
- self.fontInfoWindow = TabDialog(self.font, self)
- self.fontInfoWindow.show()
+ self.fontInfoWindow = TabDialog(self.font, self)
+ self.fontInfoWindow.show()
else:
- # Should data be rewind if user calls font info while one is open?
- # I'd say no, but this has yet to be settled.
- self.fontInfoWindow.raise_()
+ # Should data be rewind if user calls font info while one is open?
+ # I'd say no, but this has yet to be settled.
+ self.fontInfoWindow.raise_()
def fontFeatures(self):
# TODO: see up here
if not (hasattr(self, 'fontFeaturesWindow') and self.fontFeaturesWindow.isVisible()):
- self.fontFeaturesWindow = MainEditWindow(self.font, self)
- self.fontFeaturesWindow.show()
+ self.fontFeaturesWindow = MainEditWindow(self.font, self)
+ self.fontFeaturesWindow.show()
else:
- self.fontFeaturesWindow.raise_()
+ self.fontFeaturesWindow.raise_()
def spaceCenter(self):
# TODO: see up here
@@ -679,27 +546,36 @@ class MainWindow(QMainWindow):
self.spaceCenterWindow.show()
else:
self.spaceCenterWindow.raise_()
- if self.characterWidget._selection:
+ selection = self.collectionWidget.selection
+ if selection:
glyphs = []
- for item in sorted(self.characterWidget._selection):
- glyphs.append(self.characterWidget.glyphs[item])
+ for item in sorted(selection):
+ glyph = self.collectionWidget.glyphs[item]
+ glyphs.append(glyph)
self.spaceCenterWindow.setGlyphs(glyphs)
-
+
def fontGroups(self):
# TODO: see up here
if not (hasattr(self, 'fontGroupsWindow') and self.fontGroupsWindow.isVisible()):
- self.fontGroupsWindow = GroupsWindow(self.font, self)
- self.fontGroupsWindow.show()
+ self.fontGroupsWindow = GroupsWindow(self.font, self)
+ self.fontGroupsWindow.show()
else:
- self.fontGroupsWindow.raise_()
-
+ self.fontGroupsWindow.raise_()
+
+ def sortCharacters(self):
+ sortDescriptor, ok = SortDialog.getDescriptor(self, self.sortDescriptor)
+ if ok:
+ self.sortDescriptor = sortDescriptor
+
def addGlyph(self):
- gName, ok = QInputDialog.getText(self, "Add glyph", "Name of the glyph:")
- # Not overwriting existing glyphs. Should it warn in this case? (rf)
- if ok and gName != '':
- self.font.newGlyph(gName)
- self.font[gName].width = 500
- self.characterWidget.updateGlyphsFromFont()
+ glyphs = self.collectionWidget.glyphs
+ newGlyphNames, sortFont, ok = AddGlyphDialog.getNewGlyphNames(self, glyphs)
+ if ok:
+ for name in newGlyphNames:
+ # XXX: if sortFont
+ self.newStandardGlyph(name)
+ glyphs.append(self.font[name])
+ self.collectionWidget.glyphs = glyphs
def about(self):
QMessageBox.about(self, "About Me",
@@ -707,4 +583,3 @@ class MainWindow(QMainWindow):
"<p>I am a new UFO-centric font editor and I aim to bring the <b>robofab</b> " \
"ecosystem to all main operating systems, in a fast and dependency-free " \
"package.</p>")
-
diff --git a/Lib/defconQt/glyphCollectionView.py b/Lib/defconQt/glyphCollectionView.py
new file mode 100644
index 0000000..e49b32a
--- /dev/null
+++ b/Lib/defconQt/glyphCollectionView.py
@@ -0,0 +1,381 @@
+from PyQt5.QtCore import QMimeData, QRectF, QSize, Qt
+from PyQt5.QtGui import (QBrush, QColor, QDrag, QFont, QFontMetrics, QKeySequence,
+ QLinearGradient, QPainter, QPen)
+from PyQt5.QtWidgets import QApplication, QMessageBox, QScrollArea, QWidget
+import math
+
+cellGridColor = QColor(130, 130, 130)
+cellHeaderBaseColor = QColor(230, 230, 230)
+cellHeaderLineColor = QColor(220, 220, 220)
+cellHeaderHighlightLineColor = QColor(240, 240, 240)
+cellSelectionColor = QColor.fromRgbF(.2, .3, .7, .15)
+
+GlyphCellBufferHeight = .2
+GlyphCellHeaderHeight = 14
+
+# TODO: consider extracting each platform-specific thing (fonts, shortcuts) in a
+# purposed folder
+headerFont = QFont()
+headerFont.setFamily('Lucida Sans Unicode')
+headerFont.insertSubstitution('Lucida Sans Unicode', 'Lucida Grande')
+headerFont.insertSubstitution('Lucida Sans Unicode', 'Luxi Sans')
+headerFont.setPointSize(8)
+voidFont = QFont(headerFont)
+voidFont.setPointSize(24)
+metrics = QFontMetrics(headerFont)
+
+def proceedWithDeletion(self):
+ closeDialog = QMessageBox(QMessageBox.Question, "", "Delete glyphs",
+ QMessageBox.Yes | QMessageBox.No, self)
+ closeDialog.setInformativeText("Are you sure you want to delete them?")
+ closeDialog.setModal(True)
+ ret = closeDialog.exec_()
+ if ret == QMessageBox.Yes:
+ return True
+ return False
+
+"""
+A widget that presents a list of glyphs in cells.
+"""
+class GlyphCollectionWidget(QWidget):
+ def __init__(self, parent=None):
+ super(GlyphCollectionWidget, self).__init__(parent)
+ self._glyphs = []
+ # TODO: hide behind a façade
+ self.squareSize = 56
+ self._columns = 10
+ self._selection = {}
+ # TODO: consider replacing this with moveKey + set (which is generated
+ # when painting anyway)
+ self.lastKey = -1
+ self.moveKey = -1
+
+ self.characterSelectedCallback = None
+ self.doubleClickCallback = None
+ self._maybeDragPosition = None
+
+ self.setFocusPolicy(Qt.ClickFocus)
+ self._scrollArea = QScrollArea(parent)
+ self._scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
+ self._scrollArea.setWidget(self)
+
+ def _get_glyphs(self):
+ return self._glyphs
+
+ def _set_glyphs(self, value):
+ self._glyphs = value
+ self.adjustSize()
+ self.selection = set()
+ #self.update() # self.selection changed will do it
+
+ glyphs = property(_get_glyphs, _set_glyphs, doc="A list of glyphs \
+ displayed. Clears selection and schedules display refresh when set.")
+
+ def _get_selection(self):
+ return self._selection
+
+ def _set_selection(self, value):
+ self._selection = value
+ self.computeCharacterSelected()
+ self.update()
+
+ selection = property(_get_selection, _set_selection, doc="A set that contains \
+ indexes of selected glyphs. Schedules display refresh when set.")
+
+ def scrollArea(self):
+ return self._scrollArea
+
+ def scrollToCell(self, index):
+ raise NotImplementedError
+
+ # TODO: break this down into set width/set square
+ # TODO: see whether scrollArea gets resizeEvents
+ def _sizeEvent(self, width, squareSize=None):
+ sw = self._scrollArea.verticalScrollBar().width() + self._scrollArea.contentsMargins().right()
+ if squareSize is not None: self.squareSize = squareSize
+ columns = (width - sw) // self.squareSize
+ if not columns > 0: return
+ self._columns = columns
+ self.adjustSize()
+
+ def sizeHint(self):
+ # Calculate sizeHint with max(height, _scrollArea.height()) because if scrollArea is
+ # bigger than widget height after an update, we risk leaving old painted content on screen
+ return QSize(self._columns * self.squareSize,
+ max(math.ceil(len(self.glyphs) / self._columns) * self.squareSize, self._scrollArea.height()))
+
+ def computeCharacterSelected(self):
+ if self.characterSelectedCallback is None:
+ return
+ lKey, mKey = self.lastKey, self.moveKey
+ mKey = self.moveKey if self.moveKey < len(self.glyphs) else len(self.glyphs)-1
+ lKey = self.lastKey if self.lastKey < len(self.glyphs) else len(self.glyphs)-1
+ if lKey == -1:
+ elements = set()
+ elif lKey > mKey:
+ elements = set(range(mKey, lKey+1))
+ else:
+ elements = set(range(lKey, mKey+1))
+ elements ^= self.selection
+
+ cnt = len(elements)
+ if cnt == 1:
+ self.characterSelectedCallback(self.glyphs[elements.pop()].name)
+ else:
+ self.characterSelectedCallback(cnt)
+
+ def keyPressEvent(self, event):
+ key = event.key()
+ modifiers = event.modifiers()
+ if event.matches(QKeySequence.SelectAll):
+ self.selection = set(range(len(self.glyphs)))
+ elif key == Qt.Key_D and modifiers & Qt.ControlModifier:
+ self.selection = set()
+ # XXX: this is specific to fontView so should be done thru subclassing of a base widget,
+ # as is done in groupsView
+ elif key == Qt.Key_Delete:
+ #if self.characterDeletionCallback is not None:
+ if self.proceedWithDeletion() and self.selection:
+ # we need to del in reverse order to keep key references valid
+ for key in sorted(self._selection, reverse=True):
+ glyph = self.glyphs[key]
+ font = glyph.getParent()
+ if glyph in font:
+ del self.font[gName]
+ if modifiers & Qt.ShiftModifier:
+ # XXX: need a del fn in property
+ del self.glyphs[key]
+ self.selection = set()
+ else:
+ super(GlyphCollectionWidget, self).keyPressEvent(event)
+ return
+ event.accept()
+
+ def mousePressEvent(self, event):
+ if event.button() == Qt.LeftButton:
+ key = (event.y() // self.squareSize) * self._columns + event.x() // self.squareSize
+ if key > len(self.glyphs)-1: return
+ modifiers = event.modifiers()
+ if modifiers & Qt.ShiftModifier and len(self.selection) == 1:
+ self.lastKey = self.selection.pop()
+ self.moveKey = key
+ elif modifiers & Qt.ControlModifier:
+ self.lastKey = key
+ self.moveKey = self.lastKey
+ elif key in self.selection and not modifiers & Qt.ShiftModifier:
+ self._maybeDragPosition = event.pos()
+ event.accept()
+ return
+ else:
+ self.selection = set()
+ self.lastKey = key
+ self.moveKey = self.lastKey
+
+ # TODO: make sure lastKey/moveKey are taken care of before rmin this
+ self.computeCharacterSelected()
+ event.accept()
+ self.update()
+ else:
+ super(GlyphCollectionWidget, self).mousePressEvent(event)
+
+ 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.setText(glyphList)
+ drag.setMimeData(mimeData)
+
+ dropAction = drag.exec_()
+ self._maybeDragPosition = None
+ event.accept()
+ return
+ key = (event.y() // self.squareSize) * self._columns + min(event.x() // self.squareSize, self._columns-1)
+ if key < 0 or key > len(self.glyphs)-1: return
+ self.moveKey = key
+
+ self.computeCharacterSelected()
+ event.accept()
+ self.update()
+ else:
+ super(GlyphCollectionWidget, self).mouseMoveEvent(event)
+
+ def mouseReleaseEvent(self, event):
+ if event.button() == Qt.LeftButton:
+ self._maybeDragPosition = None
+ if self.lastKey == -1:
+ if self._maybeDragPosition is None:
+ key = (event.y() // self.squareSize) * self._columns + event.x() // self.squareSize
+ if key > len(self.glyphs)-1: return
+ self.selection = {key}
+ else:
+ 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 moveKey > lastKey:
+ sel = set(range(lastKey, moveKey+1))
+ else:
+ sel = set(range(moveKey, lastKey+1))
+ self.lastKey = -1
+ self.moveKey = -1
+ if event.modifiers() & Qt.ControlModifier:
+ self.selection ^= sel
+ else:
+ self.selection = sel
+ event.accept()
+ self.update()
+ else:
+ super(GlyphCollectionWidget, self).mouseReleaseEvent(event)
+
+ def mouseDoubleClickEvent(self, event):
+ if event.button() == Qt.LeftButton:
+ key = (event.y() // self.squareSize) * self._columns + event.x() // self.squareSize
+ if key > len(self.glyphs)-1: event.ignore(); return
+ self.selection -= {key}
+ self.lastKey = key
+ self.moveKey = self.lastKey
+ event.accept()
+ if self.doubleClickCallback is not None:
+ self.doubleClickCallback(self.glyphs[key])
+ else:
+ super(GlyphCollectionWidget, self).mousePressEvent(event)
+
+ # TODO: see if more of this process can be delegated to a factory
+ def paintEvent(self, event):
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing)
+
+ redrawRect = event.rect()
+ beginRow = redrawRect.top() // self.squareSize
+ endRow = redrawRect.bottom() // self.squareSize
+ # XXX: do we need to maintain self._column when we have (endColumn -
+ # beginColumn)?
+ beginColumn = redrawRect.left() // self.squareSize
+ endColumn = redrawRect.right() // self.squareSize
+
+ # selection code
+ if self.moveKey != -1:
+ if self.moveKey > self.lastKey:
+ curSelection = set(range(self.lastKey, self.moveKey+1))
+ else:
+ curSelection = set(range(self.moveKey, self.lastKey+1))
+ elif self.lastKey != -1: # XXX: necessary?
+ curSelection = {self.lastKey}
+ else:
+ curSelection = set()
+ curSelection ^= self._selection
+
+ gradient = QLinearGradient(0, 0, 0, GlyphCellHeaderHeight)
+ gradient.setColorAt(0.0, cellHeaderBaseColor)
+ gradient.setColorAt(1.0, cellHeaderLineColor)
+ dirtyGradient = QLinearGradient(0, 0, 0, GlyphCellHeaderHeight)
+ dirtyGradient.setColorAt(0.0, cellHeaderBaseColor.darker(125))
+ dirtyGradient.setColorAt(1.0, cellHeaderLineColor.darker(125))
+ markGradient = QLinearGradient(0, 0, 0, self.squareSize-GlyphCellHeaderHeight)
+
+ for row in range(beginRow, endRow + 1):
+ for column in range(beginColumn, endColumn + 1):
+ key = row * self._columns + column
+ if key > len(self.glyphs)-1: break
+ glyph = self.glyphs[key]
+
+ painter.save()
+ painter.translate(column * self.squareSize, row * self.squareSize)
+ painter.fillRect(0, 0, self.squareSize, self.squareSize, Qt.white)
+ # prepare header colors
+ brushColor = gradient
+ linesColor = cellHeaderHighlightLineColor
+ # mark color
+ if not glyph.template:
+ # TODO: fetch via defcon dict
+ if "public.markColor" in glyph.lib:
+ colorStr = glyph.lib["public.markColor"].split(",")
+ if len(colorStr) == 4:
+ comp = []
+ for c in colorStr:
+ comp.append(float(c.strip()))
+ markColor = QColor.fromRgbF(*comp)
+ markGradient.setColorAt(1.0, markColor)
+ markGradient.setColorAt(0.0, markColor.lighter(125))
+ painter.fillRect(0, GlyphCellHeaderHeight, self.squareSize,
+ self.squareSize - GlyphCellHeaderHeight, QBrush(markGradient))
+ if glyph.dirty:
+ brushColor = dirtyGradient
+ linesColor = cellHeaderHighlightLineColor.darker(110)
+
+ # header gradient
+ painter.fillRect(0, 0, self.squareSize, GlyphCellHeaderHeight,
+ QBrush(brushColor))
+ # header lines
+ painter.setPen(linesColor)
+ minOffset = painter.pen().width()
+ # disable antialiasing to avoid lines bleeding over background
+ painter.setRenderHint(QPainter.Antialiasing, False)
+ painter.drawLine(0, 0, 0, GlyphCellHeaderHeight - 1)
+ painter.drawLine(self.squareSize - 2, 0, self.squareSize - 2, GlyphCellHeaderHeight -1)
+ painter.setPen(QColor(170, 170, 170))
+ painter.drawLine(0, GlyphCellHeaderHeight, self.squareSize, GlyphCellHeaderHeight)
+ painter.setRenderHint(QPainter.Antialiasing)
+ # header text
+ painter.setFont(headerFont)
+ painter.setPen(QColor(80, 80, 80))
+ name = metrics.elidedText(glyph.name, Qt.ElideRight, self.squareSize - 2)
+ painter.drawText(1, 0, self.squareSize - 2, GlyphCellHeaderHeight - minOffset,
+ Qt.TextSingleLine | Qt.AlignCenter, name)
+ painter.restore()
+
+ painter.setPen(cellGridColor)
+ rightEdgeX = column * self.squareSize + self.squareSize
+ bottomEdgeY = row * self.squareSize + self.squareSize
+ painter.drawLine(rightEdgeX, row * self.squareSize + 1, rightEdgeX, bottomEdgeY)
+ painter.drawLine(rightEdgeX, bottomEdgeY, column * self.squareSize + 1, bottomEdgeY)
+
+ # selection code
+ painter.setRenderHint(QPainter.Antialiasing, False)
+ if key in curSelection:
+ painter.fillRect(column * self.squareSize + 1,
+ row * self.squareSize + 1, self.squareSize - 3,
+ self.squareSize - 3, cellSelectionColor)
+ painter.setRenderHint(QPainter.Antialiasing)
+
+ if not glyph.template:
+ font = glyph.getParent()
+ outline = glyph.getRepresentation("defconQt.QPainterPath")
+ uPM = font.info.unitsPerEm
+ if uPM is None or not uPM > 0:
+ uPM = 1000
+ descender = font.info.descender
+ if descender is None or not descender < 0:
+ descender = -250
+ factor = (self.squareSize-GlyphCellHeaderHeight) / (uPM*(1+2*GlyphCellBufferHeight))
+ x_offset = (self.squareSize-glyph.width*factor)/2
+ # If the glyph overflows horizontally we need to adjust the scaling factor
+ if x_offset < 0:
+ factor *= 1+2*x_offset/(glyph.width*factor)
+ x_offset = 0
+ # TODO: the * 1.8 below is somewhat artificial
+ y_offset = descender*factor * 1.8
+ painter.save()
+ painter.setClipRect(column * self.squareSize, row * self.squareSize+GlyphCellHeaderHeight,
+ self.squareSize, self.squareSize-GlyphCellHeaderHeight)
+ painter.translate(column * self.squareSize + x_offset, row * self.squareSize + self.squareSize + y_offset)
+ painter.scale(factor, -factor)
+ painter.fillPath(outline, Qt.black)
+ painter.restore()
+ else:
+ painter.save()
+ painter.setFont(voidFont)
+ painter.setPen(QPen(Qt.lightGray))
+ rect = QRectF(column * self.squareSize, row * self.squareSize+GlyphCellHeaderHeight,
+ self.squareSize, self.squareSize-GlyphCellHeaderHeight)
+ # TODO: need to flag template glyphs as to whether they have unicodings or not
+ if glyph.unicode is not None:
+ text = chr(glyph.unicode)
+ else:
+ text = "✌"
+ painter.drawText(rect, Qt.AlignCenter, text)
+ painter.restore()
diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py
index 8212a66..04a15cc 100644
--- a/Lib/defconQt/glyphView.py
+++ b/Lib/defconQt/glyphView.py
@@ -16,14 +16,13 @@ class GotoWindow(QDialog):
dict(type="alphabetical", allowPseudoUnicode=True)
]
- def __init__(self, view, parent=None):
+ def __init__(self, font, parent=None):
super(GotoWindow, self).__init__(parent)
self.setWindowModality(Qt.WindowModal)
self.setWindowTitle("Go to…")
- self._view = view
- font = self._view._font
- self._sortedGlyphs = font.unicodeData.sortGlyphNames(font.keys(), self.alphabetical)
-
+ self.font = font
+ self._sortedGlyphs = self.font.unicodeData.sortGlyphNames(self.font.keys(), self.alphabetical)
+
layout = QGridLayout(self)
self.glyphLabel = QLabel("Glyph:", self)
self.glyphEdit = QLineEdit(self)
@@ -34,13 +33,13 @@ class GotoWindow(QDialog):
self.containsBox = QRadioButton("Contains", self)
self.beginsWithBox.setChecked(True)
self.beginsWithBox.toggled.connect(self.updateGlyphList)
-
+
self.glyphList = QListWidget(self)
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
-
+
l = 0
layout.addWidget(self.glyphLabel, l, 0, 1, 2)
layout.addWidget(self.glyphEdit, l, 2, 1, 4)
@@ -67,7 +66,7 @@ class GotoWindow(QDialog):
event.accept()
else:
QLineEdit.keyPressEvent(self.glyphEdit, event)
-
+
def updateGlyphList(self, select=True):
self.glyphList.clear()
if not self.glyphEdit.isModified():
@@ -79,32 +78,31 @@ class GotoWindow(QDialog):
glyphs = [glyph for glyph in self._sortedGlyphs if text in glyph]
self.glyphList.addItems(glyphs)
if select: self.glyphList.setCurrentRow(0)
-
+
def accept(self):
# TODO: zap going thru the view, here and above
currentItem = self.glyphList.currentItem()
if currentItem is not None:
- font = self._view._font
targetGlyph = currentItem.text()
- if not targetGlyph in font: return
- self._view.setGlyph(font[targetGlyph])
+ if not targetGlyph in self.font: return
+ self._view.setGlyph(self.font[targetGlyph])
super(GotoWindow, self).accept()
class MainGfxWindow(QMainWindow):
- def __init__(self, font=None, glyph=None, parent=None):
+ def __init__(self, glyph, parent=None):
super(MainGfxWindow, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setAttribute(Qt.WA_KeyCompression)
- self.view = GlyphView(font, glyph, self)
+ self.view = GlyphView(glyph, self)
fileMenu = QMenu("&File", self)
fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
self.menuBar().addMenu(fileMenu)
-
+
toolsMenu = QMenu("&Tools", self)
-
+
self.selectTool = toolsMenu.addAction("&Selection")
self.selectTool.setCheckable(True)
self.selectTool.toggled.connect(self.view.setSceneSelection)
@@ -112,7 +110,7 @@ class MainGfxWindow(QMainWindow):
self.drawingTool = toolsMenu.addAction("&Drawing")
self.drawingTool.setCheckable(True)
self.drawingTool.toggled.connect(self.view.setSceneDrawing)
-
+
self.rulerTool = toolsMenu.addAction("&Ruler")
self.rulerTool.setCheckable(True)
self.rulerTool.toggled.connect(self.view.setSceneRuler)
@@ -170,13 +168,13 @@ class MainGfxWindow(QMainWindow):
rendererGroup.triggered.connect(self.setRenderer)
self.setCentralWidget(self.view)
- self.setWindowTitle(glyph.name, font)
+ self.setWindowTitle(glyph.name, glyph.getParent())
self.adjustSize()
-
+
def close(self):
self.view._glyph.removeObserver(self, "Glyph.Changed")
super(MainGfxWindow, self).close()
-
+
def _glyphChanged(self, notification):
self.view._glyphChanged(notification)
@@ -245,7 +243,7 @@ class OffCurvePointItem(QGraphicsEllipseItem):
self.setBrush(QBrush(offCurvePointColor))
self._needsUngrab = False
-
+
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange:
if self.scene()._integerPlane:
@@ -266,18 +264,18 @@ class OffCurvePointItem(QGraphicsEllipseItem):
elif change == QGraphicsItem.ItemPositionHasChanged:
self.parentItem()._CPMoved(value)
return value
-
+
def mousePressEvent(self, event):
if not self._needsUngrab and self.x() == 0 and self.y() == 0:
event.ignore()
super(OffCurvePointItem, self).mousePressEvent(event)
-
+
def mouseReleaseEvent(self, event):
super(OffCurvePointItem, self).mouseReleaseEvent(event)
if self._needsUngrab:
self.ungrabMouse()
self._needsUngrab = False
-
+
# http://www.qtfr.org/viewtopic.php?pid=21045#p21045
def paint(self, painter, option, widget):
#if self.x() == 0 and self.y() == 0: return
@@ -290,7 +288,7 @@ class OffCurvePointItem(QGraphicsEllipseItem):
pen.setColor(offCurvePointStrokeColor)
self.setPen(pen)
super(OffCurvePointItem, self).paint(painter, newOption, widget)
-
+
def setPointPath(self, scale=None):
if scale is None:
scene = self.scene()
@@ -318,7 +316,7 @@ class OnCurvePointItem(QGraphicsPathItem):
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
self.setBrush(QBrush(onCurvePointColor))
-
+
def delete(self, preserveShape=True):
def findNextOnCurve(self, index=0):
for _ in self._contour:
@@ -347,7 +345,7 @@ class OnCurvePointItem(QGraphicsPathItem):
self._contour.setStartPoint(nextOnCurveIndex)
scene._blocked = False
scene.removeItem(self)
-
+
def setPointPath(self, scale=None):
path = QPainterPath()
if scale is None:
@@ -365,10 +363,10 @@ class OnCurvePointItem(QGraphicsPathItem):
self.prepareGeometryChange()
self.setPath(path)
self.setPen(QPen(onCurvePointStrokeColor, onCurvePenWidth/scale))
-
+
def getPointIndex(self):
return self._contour.index(self._point)
-
+
def getSegmentIndex(self):
# closed contour cycles and so the "previous" segment goes to current point
index = 0 if self._contour.open else -1
@@ -376,7 +374,7 @@ class OnCurvePointItem(QGraphicsPathItem):
if pt == self._point: break
if pt.segmentType is not None: index += 1
return index % len(self._contour.segments)
-
+
def _CPMoved(self, newValue):
pointIndex = self.getPointIndex()
children = self.childItems()
@@ -435,13 +433,13 @@ class OnCurvePointItem(QGraphicsPathItem):
self._contour[pointIndex+1].y = self.pos().y()+nextPos.y()
self.setShallowDirty()
return value
-
+
def setShallowDirty(self):
scene = self.scene()
scene._blocked = True
self._contour.dirty = True
scene._blocked = False
-
+
def mouseMoveEvent(self, event):
modifiers = event.modifiers()
children = self.childItems()
@@ -491,13 +489,13 @@ class OnCurvePointItem(QGraphicsPathItem):
if view._currentTool == SceneTools.RulerTool or view._currentTool == SceneTools.KnifeTool:
return
self.setIsSmooth(not self._isSmooth)
-
+
def setIsSmooth(self, isSmooth):
self._isSmooth = isSmooth
self._point.smooth = self._isSmooth
self.setShallowDirty()
self.setPointPath()
-
+
# http://www.qtfr.org/viewtopic.php?pid=21045#p21045
def paint(self, painter, option, widget):
newOption = QStyleOptionGraphicsItem(option)
@@ -520,18 +518,18 @@ class ResizeHandleItem(QGraphicsRectItem):
self.setFlag(QGraphicsItem.ItemIsMovable)
#self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setCursor(Qt.SizeFDiagCursor)
-
+
rect = self.parentItem().boundingRect()
self.setPos(rect.width(), rect.height())
-
+
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
if not value: self.setVisible(value)
return value
-
+
def mouseMoveEvent(self, event):
self.parentItem()._pixmapGeometryChanged(event)
-
+
def setPointPath(self, scale=None):
if scale is None:
scene = self.scene()
@@ -558,7 +556,7 @@ class PixmapItem(QGraphicsPixmapItem):
self._rHeight = rect.height()
handle = ResizeHandleItem(self)
handle.setVisible(False)
-
+
def _pixmapGeometryChanged(self, event):
modifiers = event.modifiers()
pos = event.scenePos()
@@ -577,7 +575,7 @@ class PixmapItem(QGraphicsPixmapItem):
dx = (pos.x() - self.x()) / self._rWidth
self.setTransform(QTransform().fromScale(dx, dy))
event.accept()
-
+
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
children = self.childItems()
@@ -602,21 +600,21 @@ class GlyphScene(QGraphicsScene):
font.setFamily("Roboto Mono")
font.setFixedPitch(True)
self.setFont(font)
-
+
self._blocked = False
-
+
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super(GlyphScene, self).dragEnterEvent(event)
-
+
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super(GlyphScene, self).dragMoveEvent(event)
-
+
def dropEvent(self, event):
mimeData = event.mimeData()
if mimeData.hasUrls():
@@ -630,13 +628,13 @@ class GlyphScene(QGraphicsScene):
newPix = PixmapItem(pos.x(), pos.y(), dragPix)
self.addItem(newPix)
event.acceptProposedAction()
-
+
def getItemForPoint(self, point):
for item in self.items():
if isinstance(item, OnCurvePointItem) and item._point == point:
return item
return None
-
+
def getViewScale(self):
return self.views()[0].transform().m11()
@@ -669,24 +667,24 @@ class GlyphScene(QGraphicsScene):
event.accept()
return
elif key == Qt.Key_J:
- view = self.views()[0]
- dialog = GotoWindow(view, self.parent())
+ glyph = self.views()[0]._glyph
+ dialog = GotoWindow(glyph.getParent(), self.parent())
dialog.exec_()
return
elif event.matches(QKeySequence.Undo):
if len(self._dataForUndo) > 0:
undo = self._dataForUndo.pop()
- redo = self._glyphObject.getDataToSerializeForUndo()
- self._glyphObject.loadDeserializedDataFromUndo(undo)
+ redo = self._glyphObject.serializeForUndo()
+ self._glyphObject.deserializeFromUndo(undo)
self._dataForRedo.append(redo)
event.accept()
return
elif event.matches(QKeySequence.Redo):
if len(self._dataForRedo) > 0:
- undo = self._glyphObject.getDataToSerializeForUndo()
+ undo = self._glyphObject.serializeForUndo()
redo = self._dataForRedo.pop()
self._dataForUndo.append(undo)
- self._glyphObject.loadDeserializedDataFromUndo(redo)
+ self._glyphObject.deserializeFromUndo(redo)
event.accept()
return
elif event.matches(QKeySequence.SelectAll):
@@ -718,7 +716,7 @@ class GlyphScene(QGraphicsScene):
if isinstance(item, OffCurvePointItem) and item.parentItem().isSelected(): continue
item.moveBy(x,y)
event.accept()
-
+
def keyReleaseEvent(self, event):
sel = self.selectedItems()
if len(sel) == 1 and isinstance(sel[0], OffCurvePointItem) and \
@@ -734,7 +732,7 @@ class GlyphScene(QGraphicsScene):
self.rulerMousePress(event)
return
else:
- data = self._glyphObject.getDataToSerializeForUndo()
+ data = self._glyphObject.serializeForUndo()
self._dataForUndo.append(data)
self._dataForRedo = []
if view._currentTool == SceneTools.KnifeTool:
@@ -815,7 +813,7 @@ class GlyphScene(QGraphicsScene):
super(GlyphScene, self).mousePressEvent(event)
# Since shift clamps, we might be missing the point in mousePressEvent
if forceSelect: item.setSelected(True)
-
+
def mouseMoveEvent(self, event):
if self._editing is True:
sel = self.selectedItems()
@@ -846,7 +844,7 @@ class GlyphScene(QGraphicsScene):
self.sendEvent(sel[0], QEvent(QEvent.MouseButtonRelease))
mouseGrabberItem.ungrabMouse()
sel[0].setSelected(False)
-
+
# construct a curve segment to the current point if there is not one
onCurve = sel[0]._point
if not onCurve.segmentType == "curve":
@@ -906,7 +904,7 @@ class GlyphScene(QGraphicsScene):
items[1].setPos(0, 0)
else:
super(GlyphScene, self).mouseMoveEvent(event)
-
+
def mouseReleaseEvent(self, event):
self._editing = False
currentTool = self.views()[0]._currentTool
@@ -929,7 +927,7 @@ class GlyphScene(QGraphicsScene):
elif currentTool == SceneTools.RulerTool:
self.rulerMouseRelease(event)
super(GlyphScene, self).mouseReleaseEvent(event)
-
+
def rulerMousePress(self, event):
touched = self.itemAt(event.scenePos(), self.views()[0].transform())
if touched is not None and isinstance(touched, OnCurvePointItem) or \
@@ -955,7 +953,7 @@ class GlyphScene(QGraphicsScene):
textItem.setFlag(QGraphicsItem.ItemIgnoresTransformations)
textItem.setPos(x, y + textItem.boundingRect().height())
event.accept()
-
+
def rulerMouseMove(self, event):
# XXX: shouldnt have to do this, it seems mouseTracking is wrongly activated
if self._rulerObject is None: return
@@ -995,7 +993,7 @@ class GlyphScene(QGraphicsScene):
else: py = baseElem.y + textItem.boundingRect().height()
textItem.setPos(px, py)
event.accept()
-
+
def rulerMouseRelease(self, event):
self._cachedRuler = self._rulerObject
self._rulerObject = None
@@ -1101,16 +1099,15 @@ class GlyphScene(QGraphicsScene):
class GlyphView(QGraphicsView):
Native, OpenGL, Image = range(3)
- def __init__(self, font, glyph, parent=None):
+ def __init__(self, glyph, parent=None):
super(GlyphView, self).__init__(parent)
self.renderer = GlyphView.Native
- self._font = font
self._glyph = glyph
self._glyph.addObserver(self, "_glyphChanged", "Glyph.Changed")
self._impliedPointSize = 1000
self._pointSize = None
-
+
self._inverseScale = 0.1
self._scale = 10
self._noPointSizePadding = 200
@@ -1118,8 +1115,8 @@ class GlyphView(QGraphicsView):
self._drawStroke = True
self._showOffCurvePoints = True
self._showOnCurvePoints = True
- self._fillColor = QColor.fromRgbF(0, 0, 0, .4)
- self._componentFillColor = QColor.fromRgbF(.2, .2, .3, .4)
+ self._fillColor = QColor(200, 200, 200, 120)#QColor.fromRgbF(0, 0, 0, .4)
+ self._componentFillColor = QColor.fromRgbF(0, 0, 0, .4)#QColor.fromRgbF(.2, .2, .3, .4)
self._showMetricsTitles = True
self._metricsColor = QColor(70, 70, 70)
@@ -1129,30 +1126,29 @@ class GlyphView(QGraphicsView):
font.setFamily("Roboto Mono")
font.setFixedPitch(True)
self.setFont(font)
-
+
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
#self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
-
+
self._drawingTool = SceneTools.SelectionTool
self.setDragMode(QGraphicsView.RubberBandDrag)
-
+
self.setRenderHint(QPainter.Antialiasing)
- #self.translate(0, self.height()*(1+self._font.info.descender/self._font.info.unitsPerEm))
self.scale(1, -1)
self.addBackground()
self.addBlues()
self.addHorizontalMetrics()
self.addOutlines()
self.addPoints()
-
+
def _glyphChanged(self, notification):
# TODO: maybe detect sidebearing changes (space center) and then only
# translate elements rather than reconstructing them.
# Also we lose selection when reconstructing, rf does not when changing
# sp.center values.
self.redrawGlyph()
-
+
def redrawGlyph(self):
path = self._glyph.getRepresentation("defconQt.NoComponentsQPainterPath")
scene = self.scene()
@@ -1172,42 +1168,16 @@ class GlyphView(QGraphicsView):
# this will not be the case anymore when drag sidebearings pops up
scene._widthItem.setRect(0, -1000, self._glyph.width, 3000)
- def _getGlyphWidthHeight(self):
- if self._glyph.bounds:
- left, bottom, right, top = self._glyph.bounds
- else:
- left = right = bottom = top = 0
- left = min((0, left))
- right = max((right, self._glyph.width))
- bottom = self._font.info.descender
- top = max((self._font.info.capHeight, self._font.info.ascender, self._font.info.unitsPerEm + self._font.info.descender))
- width = abs(left) + right
- height = -bottom + top
- return width, height
-
- def _calcScale(self):
- if self._pointSize is None:
- visibleHeight = self.viewport().height()
- fitHeight = visibleHeight
- glyphWidth, glyphHeight = self._getGlyphWidthHeight()
- glyphHeight += self._noPointSizePadding * 2
- self._scale = fitHeight / glyphHeight
- else:
- self._scale = self._pointSize / float(self._font.info.unitsPerEm)
- if self._scale <= 0:
- self._scale = .01
- self._inverseScale = 1.0 / self._scale
- self._impliedPointSize = self._font.info.unitsPerEm * self._scale
-
def addBackground(self):
scene = self.scene()
+ font = self._glyph.getParent()
width = self._glyph.width
item = scene.addRect(-1000, -1000, 3000, 3000, QPen(Qt.black), QBrush(Qt.gray))
item.setZValue(-1000)
scene._widthItem = scene.addRect(0, -1000, width, 3000, QPen(Qt.NoPen), QBrush(backgroundColor))
scene._widthItem.setZValue(-999)
- self.centerOn(width/2, self._font.info.descender+self._font.info.unitsPerEm/2)
-
+ self.centerOn(width/2, font.info.descender+font.info.unitsPerEm/2)
+
def addBlues(self):
scene = self.scene()
#width = self._glyph.width# * self._inverseScale
@@ -1229,16 +1199,17 @@ class GlyphView(QGraphicsView):
else:
item = scene.addRect(-1000, yMin, 3000, yMax - yMin, QPen(Qt.NoPen), QBrush(self._bluesColor))
item.setZValue(-998)
-
+
def addHorizontalMetrics(self):
scene = self.scene()
+ font = self._glyph.getParent()
width = self._glyph.width# * self._inverseScale
toDraw = [
- ("Descender", self._font.info.descender),
+ ("Descender", font.info.descender),
("Baseline", 0),
- ("x-height", self._font.info.xHeight),
- ("Cap height", self._font.info.capHeight),
- ("Ascender", self._font.info.ascender)
+ ("x-height", font.info.xHeight),
+ ("Cap height", font.info.capHeight),
+ ("Ascender", font.info.ascender)
]
positions = {}
for name, position in toDraw:
@@ -1376,15 +1347,18 @@ class GlyphView(QGraphicsView):
value = round(value) - .5
value = value * self._inverseScale
return value
-
- def setGlyph(self, glyph, font=None):
- if font is not None: self._font = font
+
+ def setGlyph(self, glyph):
self._glyph.removeObserver(self, "Glyph.Changed")
+ # TODO: consider creating a new scene instead of zeroing things out
+ # manually
+ self._dataForUndo = []
+ self._dataForRedo = []
self._glyph = glyph
+ # XXX: DRY ALERT!
+ self.scene()._glyphObject = glyph
self._glyph.addObserver(self, "_glyphChanged", "Glyph.Changed")
- #self.scene().setSceneRect(*self._glyph.bounds)
- #self.scene().setSceneRect(0, self._font.info.ascender, self._glyph.width, self._font.info.unitsPerEm)
- self.parent().setWindowTitle(glyph.name, self._font)
+ self.parent().setWindowTitle(self._glyph.name, self._glyph.getParent())
self.redrawGlyph()
def setRenderer(self, renderer):
@@ -1403,7 +1377,7 @@ class GlyphView(QGraphicsView):
def setViewOutline(self, enable):
if self.outlineItem:
self.outlineItem.setVisible(enable)
-
+
def mousePressEvent(self, event):
if (event.button() == Qt.MidButton):
dragMode = self.dragMode()
@@ -1416,11 +1390,11 @@ class GlyphView(QGraphicsView):
def setSceneDrawing(self):
self._currentTool = SceneTools.DrawingTool
self.setDragMode(QGraphicsView.NoDrag)
-
+
def setSceneRuler(self):
self._currentTool = SceneTools.RulerTool
self.setDragMode(QGraphicsView.NoDrag)
-
+
def setSceneSelection(self):
self._currentTool = SceneTools.SelectionTool
self.setDragMode(QGraphicsView.RubberBandDrag)
@@ -1440,8 +1414,6 @@ class GlyphView(QGraphicsView):
def wheelEvent(self, event):
factor = pow(1.2, event.angleDelta().y() / 120.0)
- #self._calcScale()
- #self._setFrame()
self.scale(factor, factor)
# TODO: stop displaying SimpleTextItems at certains sizes, maybe anchor them differently as well
scale = self.transform().m11()
@@ -1452,17 +1424,3 @@ class GlyphView(QGraphicsView):
item.setPointPath(scale)
self.update()
event.accept()
-
-if __name__ == '__main__':
-
- import sys
-
- app = QApplication(sys.argv)
-
- window = MainGfxWindow()
- if len(sys.argv) == 2:
- window.openFile(sys.argv[1])
- else:
- window.openFile(':/files/bubbles.svg')
- window.show()
- sys.exit(app.exec_())
diff --git a/Lib/defconQt/groupsView.py b/Lib/defconQt/groupsView.py
index bec64dc..e6ee932 100644
--- a/Lib/defconQt/groupsView.py
+++ b/Lib/defconQt/groupsView.py
@@ -1,4 +1,4 @@
-from defconQt.fontView import CharacterWidget
+from defconQt.glyphCollectionView import GlyphCollectionWidget
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
@@ -9,12 +9,12 @@ class GroupListWidget(QListWidget):
#self.setAlternatingRowColors(True)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setSortingEnabled(True)
-
+
for groupName in groupNames:
item = QListWidgetItem(groupName, self)
item.setFlags(item.flags() | Qt.ItemIsEditable)
#if len(groupNames): self.setCurrentRow(0)
-
+
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Delete:
@@ -36,19 +36,19 @@ class GroupStackWidget(QWidget):
self.upm = font.info.unitsPerEm
self.padding = 10
self.alignRight = False
-
+
def setAlignment(self, alignRight):
self.alignRight = alignRight
self.update()
-
+
def setGlyphs(self, glyphs):
self.glyphs = glyphs
self.maxWidth = max(glyph.width for glyph in self.glyphs) if len(self.glyphs) else 300
self.update()
-
+
def sizeHint(self):
return QSize(self.maxWidth+2*self.padding, 400)
-
+
def paintEvent(self, event):
# TODO: maybe use self.upm*(1+2*BufferHeight) for the denominator as in fontView
scale = self.height() / (self.upm*1.2)
@@ -60,7 +60,7 @@ class GroupStackWidget(QWidget):
painter.setRenderHint(QPainter.Antialiasing)
painter.translate(self.padding, self.padding+(self.ascender*1.2)*scale)
painter.scale(scale, -scale)
-
+
col = QColor(Qt.black)
col.setAlphaF(.2)
for glyph in self.glyphs:
@@ -72,32 +72,38 @@ class GroupStackWidget(QWidget):
painter.fillPath(glyphPath, col)
painter.restore()
-class GroupCharacterWidget(CharacterWidget):
- def __init__(self, font, squareSize=56, scrollArea=None, parent=None):
- super(GroupCharacterWidget, self).__init__(font, squareSize, scrollArea, parent)
- self.columns = 9
- self.scrollArea.setAcceptDrops(True)
- self.scrollArea.dragEnterEvent = self.pipeDragEnterEvent
- self.scrollArea.dropEvent = self.pipeDropEvent
+class GroupCollectionWidget(GlyphCollectionWidget):
+ def __init__(self, parent=None):
+ super(GroupCollectionWidget, self).__init__(parent)
+ self._columns = 9
+ self._scrollArea.setAcceptDrops(True)
+ self._scrollArea.dragEnterEvent = self.pipeDragEnterEvent
+ self._scrollArea.dropEvent = self.pipeDropEvent
+
+ # TODO: upstream this, somehow
+ self.characterDeletionCallback = None
+ self.characterDropCallback = None
self.resize(self.width(), 200)
-
+
# TODO: The standard QListWidget has scrollbar and does not need three times parent call.
# Find out how to handle that properly.
def keyPressEvent(self, event):
if event.key() == Qt.Key_Delete:
- self.parent().parent().parent().characterDeleteEvent(self._selection)
+ if self.characterDeletionCallback is not None:
+ self.characterDeletionCallback(self.selection)
event.accept()
else:
- super(GroupCharacterWidget, self).keyPressEvent(event)
-
+ super(GroupCollectionWidget, 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().hasText()):
event.acceptProposedAction()
-
+
def pipeDropEvent(self, event):
- self.parent().parent().parent().characterDropEvent(event)
+ if self.characterDropCallback is not None:
+ self.characterDropCallback(event)
class GroupsWindow(QWidget):
leftGroups = ["@MMK_L", "public.kern1"]
@@ -112,9 +118,9 @@ class GroupsWindow(QWidget):
self.groupsList.currentItemChanged.connect(self._groupChanged)
self.groupsList.itemChanged.connect(self._groupRenamed)
self.groupsList.setFocus(True)
-
+
self.stackWidget = GroupStackWidget(self.font, parent=self)
-
+
self.addGroupButton = QPushButton("+", self)
self.addGroupButton.clicked.connect(self._groupAdd)
self.removeGroupButton = QPushButton("−", self)
@@ -126,12 +132,12 @@ class GroupsWindow(QWidget):
self.alignLeftBox.setChecked(True)
self.alignLeftBox.toggled.connect(self._alignmentChanged)
self._autoDirection = True
-
- self.scrollArea = QScrollArea(self)
- self.characterWidget = GroupCharacterWidget(self.font, scrollArea=self.scrollArea, parent=self)
- self.scrollArea.setWidget(self.characterWidget)
+
+ self.collectionWidget = GroupCollectionWidget(parent=self)
+ self.collectionWidget.characterDeletionCallback = self.characterDeleteEvent
+ self.collectionWidget.characterDropCallback = self.characterDropEvent
self._cachedName = None
-
+
layout = QGridLayout(self)
layout.addWidget(self.groupsList, 0, 0, 5, 4)
layout.addWidget(self.stackWidget, 0, 4, 5, 4)
@@ -139,17 +145,17 @@ class GroupsWindow(QWidget):
layout.addWidget(self.removeGroupButton, 5, 3)
layout.addWidget(self.alignLeftBox, 5, 4)
layout.addWidget(self.alignRightBox, 5, 7)
- layout.addWidget(self.scrollArea, 6, 0, 4, 8)
+ layout.addWidget(self.collectionWidget.scrollArea(), 6, 0, 4, 8)
# TODO: calib this more
layout.setColumnStretch(4, 1)
self.setLayout(layout)
-
- self.setWindowTitle("%s%s%s%s" % ("Groups window – ", self.font.info.familyName, " ", self.font.info.styleName))
-
+
+ self.setWindowTitle("Groups window – %s %s" % (self.font.info.familyName, self.font.info.styleName))
+
def _alignmentChanged(self):
alignRight = self.alignRightBox.isChecked()
self.stackWidget.setAlignment(alignRight)
-
+
def _groupAdd(self):
groupName = "New group"
if groupName in self.font.groups:
@@ -163,7 +169,7 @@ class GroupsWindow(QWidget):
self.groupsList.setCurrentItem(item)
self.groupsList.editItem(item)
self.removeGroupButton.setEnabled(True)
-
+
def _groupChanged(self):
self._cachedName = self.groupsList.currentItem().text()
if self._autoDirection:
@@ -180,23 +186,22 @@ class GroupsWindow(QWidget):
if gName in self.font:
glyphs.append(self.font[gName])
self.stackWidget.setGlyphs(glyphs)
- self.characterWidget.setGlyphs(glyphs)
- self.characterWidget.update()
-
+ self.collectionWidget.glyphs = glyphs
+
def _groupRenamed(self):
newKey = self.groupsList.currentItem()
if newKey is None: return
newKey = newKey.text()
self.font.groups[newKey] = self.font.groups[self._cachedName]
del self.font.groups[self._cachedName]
-
+
def _groupDelete(self):
newKey = self.groupsList.currentItem().text()
del self.font.groups[newKey]
self.groupsList.takeItem(self.groupsList.currentRow())
if not self.font.groups.keys(): self.removeGroupButton.setEnabled(False)
self._groupChanged()
-
+
def characterDeleteEvent(self, selection):
currentGroup = self.groupsList.currentItem().text()
currentGroupList = self.font.groups[currentGroup]
@@ -206,7 +211,7 @@ class GroupsWindow(QWidget):
del currentGroupList[key]
self.font.groups[currentGroup] = currentGroupList
self._groupChanged()
-
+
def characterDropEvent(self, event):
currentGroup = self.groupsList.currentItem()
if currentGroup is None: return
@@ -220,11 +225,11 @@ class GroupsWindow(QWidget):
self.font.groups[currentGroup] = currentGroupList
event.acceptProposedAction()
self._groupChanged()
-
+
def resizeEvent(self, event):
if self.isVisible():
margins = self.layout().contentsMargins()
width = event.size().width() - (margins.left() + margins.right())
- self.characterWidget._sizeEvent(width)
+ self.collectionWidget._sizeEvent(width)
self.stackWidget.update()
- super(GroupsWindow, self).resizeEvent(event) \ No newline at end of file
+ super(GroupsWindow, self).resizeEvent(event)
diff --git a/Lib/defconQt/objects/__init__.py b/Lib/defconQt/objects/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/defconQt/objects/__init__.py
diff --git a/Lib/defconQt/objects/defcon.py b/Lib/defconQt/objects/defcon.py
new file mode 100644
index 0000000..487865a
--- /dev/null
+++ b/Lib/defconQt/objects/defcon.py
@@ -0,0 +1,22 @@
+class CharacterSet(object):
+ __slots__ = ["_name", "_glyphNames"]
+
+ def __init__(self, glyphNames, name=None):
+ self._name = name
+ self._glyphNames = glyphNames
+
+ def _get_name(self):
+ return self._name
+
+ def _set_name(self, name):
+ self._name = name
+
+ name = property(_get_name, _set_name, doc="Character set name.")
+
+ def _get_glyphNames(self):
+ return self._glyphNames
+
+ def _set_glyphNames(self, glyphNames):
+ self._glyphNames = glyphNames
+
+ glyphNames = property(_get_glyphNames, _set_glyphNames, doc="List of glyph names.")
diff --git a/Lib/defconQt/spaceCenter.py b/Lib/defconQt/spaceCenter.py
index d0ba766..8036e79 100644
--- a/Lib/defconQt/spaceCenter.py
+++ b/Lib/defconQt/spaceCenter.py
@@ -1,4 +1,4 @@
-from defconQt.fontView import cellSelectionColor
+from defconQt.glyphCollectionView import cellSelectionColor
from defconQt.glyphView import MainGfxWindow
from getpass import getuser
from PyQt5.QtCore import *#QAbstractTableModel, QEvent, QSize, Qt
@@ -25,18 +25,16 @@ class MainSpaceWindow(QWidget):
self.glyphs = []
self._subscribeToGlyphsText(string)
self.toolbar = FontToolBar(string, pointSize, self)
- self.scrollArea = QScrollArea(self)
- self.canvas = GlyphsCanvas(self.font, self.glyphs, self.scrollArea, pointSize, self)
- self.scrollArea.setWidget(self.canvas)
+ self.canvas = GlyphsCanvas(self.font, self.glyphs, pointSize, self)
self.table = SpaceTable(self.glyphs, self)
- self.canvas.setDoubleClickCallback(self._glyphOpened)
- self.canvas.setPointSizeCallback(self.toolbar.setPointSize)
- self.canvas.setSelectionCallback(self.table.setCurrentGlyph)
- self.table.setSelectionCallback(self.canvas.setSelected)
-
+ self.canvas.doubleClickCallback = self._glyphOpened
+ self.canvas.pointSizeCallback = self.toolbar.setPointSize
+ self.canvas.selectionChangedCallback = self.table.setCurrentGlyph
+ self.table.selectionChangedCallback = self.canvas.setSelected
+
layout = QVBoxLayout(self)
layout.addWidget(self.toolbar)
- layout.addWidget(self.scrollArea)
+ layout.addWidget(self.canvas.scrollArea())
layout.addWidget(self.table)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
@@ -44,7 +42,7 @@ class MainSpaceWindow(QWidget):
self.resize(600, 500)
self.toolbar.comboBox.currentIndexChanged[str].connect(self.canvas.setPointSize)
self.toolbar.textField.textEdited.connect(self._textChanged)
-
+
self.font.info.addObserver(self, "_fontInfoChanged", "Info.Changed")
self.setWindowTitle("Space center – %s %s" % (self.font.info.familyName, self.font.info.styleName))
@@ -55,22 +53,22 @@ class MainSpaceWindow(QWidget):
fileMenu.addAction("&Save...", self.save, QKeySequence.Save)
fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
-
+
def close(self):
self.font.info.removeObserver(self, "Info.Changed")
self._unsubscribeFromGlyphs()
super(MainSpaceWindow, self).close()
-
+
def _fontInfoChanged(self, notification):
self.canvas.fetchFontMetrics()
self.canvas.update()
-
+
def _glyphChanged(self, notification):
self.canvas.update()
self.table.updateCells()
-
+
def _glyphOpened(self, glyph):
- glyphViewWindow = MainGfxWindow(self.font, glyph, self.parent())
+ glyphViewWindow = MainGfxWindow(glyph, self.parent())
glyphViewWindow.show()
def _textChanged(self, newText):
@@ -81,7 +79,7 @@ class MainSpaceWindow(QWidget):
# set the records into the view
self.canvas.setGlyphs(self.glyphs)
self.table.setGlyphs(self.glyphs)
-
+
# Tal Leming. Edited.
def textToGlyphNames(self, text):
# escape //
@@ -118,7 +116,7 @@ class MainSpaceWindow(QWidget):
if compileStack is not None and compileStack:
glyphNames.append("".join(compileStack))
return glyphNames
-
+
def _subscribeToGlyphsText(self, newText):
glyphs = []
glyphNames = self.textToGlyphNames(newText)
@@ -127,7 +125,7 @@ class MainSpaceWindow(QWidget):
if gName not in self.font: continue
glyphs.append(self.font[gName])
self._subscribeToGlyphs(glyphs)
-
+
def _subscribeToGlyphs(self, glyphs):
self.glyphs = glyphs
@@ -137,7 +135,7 @@ class MainSpaceWindow(QWidget):
continue
handledGlyphs.add(glyph)
glyph.addObserver(self, "_glyphChanged", "Glyph.Changed")
-
+
def _unsubscribeFromGlyphs(self):
handledGlyphs = set()
for glyph in self.glyphs:
@@ -159,7 +157,7 @@ class MainSpaceWindow(QWidget):
# set the records into the view
self.canvas.setGlyphs(self.glyphs)
self.table.setGlyphs(self.glyphs)
-
+
def resizeEvent(self, event):
if self.isVisible(): self.canvas._sizeEvent(event)
super(MainSpaceWindow, self).resizeEvent(event)
@@ -179,7 +177,7 @@ class FontToolBar(QToolBar):
self.configBar = QPushButton(self)
self.configBar.setFlat(True)
- self.configBar.setIcon(QIcon("resources/ic_settings_24px.svg"))
+ self.configBar.setIcon(QIcon("defconQt/resources/ic_settings_24px.svg"))
self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px");
self.toolsMenu = QMenu(self)
showKerning = self.toolsMenu.addAction("Show Kerning", self.showKerning)
@@ -205,34 +203,35 @@ class FontToolBar(QToolBar):
self.addWidget(self.textField)
self.addWidget(self.comboBox)
self.addWidget(self.configBar)
-
+
def setPointSize(self, pointSize):
self.comboBox.blockSignals(True)
self.comboBox.setEditText(str(pointSize))
self.comboBox.blockSignals(False)
-
+
def showKerning(self):
action = self.sender()
self.parent().canvas.setShowKerning(action.isChecked())
-
+
def showMetrics(self):
action = self.sender()
self.parent().canvas.setShowMetrics(action.isChecked())
-
+
def verticalFlip(self):
action = self.sender()
self.parent().canvas.setVerticalFlip(action.isChecked())
def wrapLines(self):
self.parent().canvas.setWrapLines(True)
-
+
def noWrapLines(self):
self.parent().canvas.setWrapLines(False)
class GlyphsCanvas(QWidget):
- def __init__(self, font, glyphs, scrollArea, pointSize=defaultPointSize, parent=None):
+ def __init__(self, font, glyphs, pointSize=defaultPointSize, parent=None):
super(GlyphsCanvas, self).__init__(parent)
-
+ # XXX: make canvas font-agnostic as in defconAppkit and use
+ # glyph.getParent() instead
self.font = font
self.fetchFontMetrics()
self.glyphs = glyphs
@@ -244,29 +243,33 @@ class GlyphsCanvas(QWidget):
self._verticalFlip = False
self._positions = None
self._selected = None
- self._doubleClickCallback = None
- self._pointSizeChangedCallback = None
- self._selectionChangedCallback = None
-
+ self.doubleClickCallback = None
+ self.pointSizeChangedCallback = None
+ self.selectionChangedCallback = None
+
self._wrapLines = True
- self.scrollArea = scrollArea
- self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
+ self._scrollArea = QScrollArea(self.parent())
+ self._scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
+ self._scrollArea.setWidget(self)
self.resize(581, 400)
+ def scrollArea(self):
+ return self._scrollArea
+
def calculateScale(self):
scale = self.ptSize / self.upm
if scale < .01: scale = 0.01
self.scale = scale
-
+
def setShowKerning(self, showKerning):
self._showKerning = showKerning
self.update()
-
+
def setShowMetrics(self, showMetrics):
self._showMetrics = showMetrics
self.update()
-
+
def setVerticalFlip(self, verticalFlip):
self._verticalFlip = verticalFlip
self.update()
@@ -275,17 +278,17 @@ class GlyphsCanvas(QWidget):
if self._wrapLines == wrapLines: return
self._wrapLines = wrapLines
if self._wrapLines:
- sw = self.scrollArea.verticalScrollBar().width() + self.scrollArea.contentsMargins().right()
+ sw = self._scrollArea.verticalScrollBar().width() + self._scrollArea.contentsMargins().right()
self.resize(self.parent().parent().parent().width() - sw, self.height())
- self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
+ self._scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
else:
- sh = self.scrollArea.horizontalScrollBar().height() + self.scrollArea.contentsMargins().bottom()
+ sh = self._scrollArea.horizontalScrollBar().height() + self._scrollArea.contentsMargins().bottom()
self.resize(self.width(), self.parent().parent().parent().height() - sh)
- self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
+ self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self._scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.update()
-
+
def fetchFontMetrics(self):
self.ascender = self.font.info.ascender
if self.ascender is None: self.ascender = 750
@@ -293,20 +296,17 @@ class GlyphsCanvas(QWidget):
if self.descender is None: self.descender = 250
self.upm = self.font.info.unitsPerEm
if self.upm is None or not self.upm > 0: self.upm = 1000
-
+
def setGlyphs(self, newGlyphs):
self.glyphs = newGlyphs
self._selected = None
self.update()
-
+
def setPointSize(self, pointSize):
self.ptSize = int(pointSize)
self.calculateScale()
self.update()
-
- def setPointSizeCallback(self, pointSizeChangedCallback):
- self._pointSizeChangedCallback = pointSizeChangedCallback
-
+
def setSelected(self, selected):
self._selected = selected
if self._positions is not None:
@@ -321,20 +321,17 @@ class GlyphsCanvas(QWidget):
if line > -1:
x = self.padding + pos + width/2
y = self.padding + (line+.5)*self.ptSize
- self.scrollArea.ensureVisible(x, y, width/2+20, .5*self.ptSize+20)
+ self._scrollArea.ensureVisible(x, y, width/2+20, .5*self.ptSize+20)
self.update()
-
- def setSelectionCallback(self, selectionChangedCallback):
- self._selectionChangedCallback = selectionChangedCallback
-
+
def _sizeEvent(self, event):
if self._wrapLines:
- sw = self.scrollArea.verticalScrollBar().width() + self.scrollArea.contentsMargins().right()
+ sw = self._scrollArea.verticalScrollBar().width() + self._scrollArea.contentsMargins().right()
self.resize(event.size().width() - sw, self.height())
else:
- sh = self.scrollArea.horizontalScrollBar().height() + self.scrollArea.contentsMargins().bottom()
+ sh = self._scrollArea.horizontalScrollBar().height() + self._scrollArea.contentsMargins().bottom()
self.resize(self.width(), event.size().height() - sh)
-
+
def wheelEvent(self, event):
if event.modifiers() & Qt.ControlModifier:
# TODO: should it snap to predefined pointSizes? is the scaling factor okay?
@@ -347,12 +344,12 @@ class GlyphsCanvas(QWidget):
if newPointSize <= 0: return
self.setPointSize(newPointSize)
- if self._pointSizeChangedCallback is not None:
- self._pointSizeChangedCallback(newPointSize)
+ if self.pointSizeChangedCallback is not None:
+ self.pointSizeChangedCallback(newPointSize)
event.accept()
else:
super(GlyphsCanvas, self).wheelEvent(event)
-
+
# Tal Leming. Edited.
def lookupKerningValue(self, first, second):
kerning = self.font.kerning
@@ -393,7 +390,7 @@ class GlyphsCanvas(QWidget):
if pair in kerning:
return kerning[pair]
return 0
-
+
def mousePressEvent(self, event):
# Take focus to quit eventual cell editing
# XXX: shouldnt set focus if we are in input field...
@@ -404,15 +401,15 @@ class GlyphsCanvas(QWidget):
else:
baselineShift = self.ascender
found = False
- line = (event.y() - self.padding) // (self.ptSize*self._lineHeight)
+ line = (event.y() - self.padding) // (self.ptSize)
# XXX: Shouldnt // yield an int?
line = int(line)
if line >= len(self._positions):
self._selected = None
# XXX: find a way to DRY notification of self._selected changed
# w ability to block notifications as well
- if self._selectionChangedCallback is not None:
- self._selectionChangedCallback(self._selected)
+ if self.selectionChangedCallback is not None:
+ self.selectionChangedCallback(self._selected)
event.accept()
self.update()
return
@@ -426,22 +423,19 @@ class GlyphsCanvas(QWidget):
self._selected = count+index
found = True
if not found: self._selected = None
- if self._selectionChangedCallback is not None:
- self._selectionChangedCallback(self._selected)
+ if self.selectionChangedCallback is not None:
+ self.selectionChangedCallback(self._selected)
event.accept()
self.update()
else:
super(GlyphCanvas, self).mousePressEvent(event)
-
+
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton and self._selected is not None:
- if self._doubleClickCallback is not None:
- self._doubleClickCallback(self.glyphs[self._selected])
+ if self.doubleClickCallback is not None:
+ self.doubleClickCallback(self.glyphs[self._selected])
else:
super(GlyphCanvas, self).mouseDoubleClickEvent(event)
-
- def setDoubleClickCallback(self, doubleClickCallback):
- self._doubleClickCallback = doubleClickCallback
def paintEvent(self, event):
linePen = QPen(Qt.black)
@@ -455,7 +449,7 @@ class GlyphsCanvas(QWidget):
painter.drawLine(0, 0, width, 0)
painter.drawLine(0, self.descender, width, self.descender)
painter.restore()
-
+
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.fillRect(0, 0, self.width(), self.height(), Qt.white)
@@ -480,7 +474,7 @@ class GlyphsCanvas(QWidget):
kern = self.lookupKerningValue(self.glyphs[index-1].name, glyph.name)*self.scale
else: kern = 0
if self._wrapLines and cur_width + gWidth + kern + 2*self.padding > self.width():
- painter.translate(-cur_width, self.ptSize*self._lineHeight)
+ painter.translate(-cur_width, self.ptSize)
if self._showMetrics: paintLineMarks(painter)
self._positions.append([(0, gWidth)])
cur_width = gWidth
@@ -502,14 +496,14 @@ class GlyphsCanvas(QWidget):
painter.fillPath(glyphPath, Qt.black)
painter.restore()
painter.translate(gWidth, 0)
-
- scrollMargins = self.scrollArea.contentsMargins()
- innerHeight = self.scrollArea.height() - scrollMargins.top() - scrollMargins.bottom()
+
+ scrollMargins = self._scrollArea.contentsMargins()
+ innerHeight = self._scrollArea.height() - scrollMargins.top() - scrollMargins.bottom()
if not self._wrapLines:
- innerWidth = self.scrollArea.width() - scrollMargins.left() - scrollMargins.right()
+ innerWidth = self._scrollArea.width() - scrollMargins.left() - scrollMargins.right()
width = max(innerWidth, cur_width+self.padding*2)
else: width = self.width()
- self.resize(width, max(innerHeight, lines*self.ptSize+2*self.padding))
+ self.resize(width, max(innerHeight, lines*self.ptSize*self._lineHeight+2*self.padding))
class SpaceTableWidgetItem(QTableWidgetItem):
def setData(self, role, value):
@@ -526,7 +520,7 @@ class GlyphCellItemDelegate(QStyledItemDelegate):
#editor.setAlignment(Qt.AlignCenter)
editor.setValidator(QIntValidator(self))
return editor
-
+
# TODO: implement =... lexer
# TODO: Alt+left or Alt+right don't SelectAll of the new cell
# cell by default. Implement this.
@@ -591,14 +585,14 @@ class SpaceTable(QTableWidget):
# edit cell on single click, not double
self.setEditTriggers(QAbstractItemView.CurrentChanged)
self._blocked = False
- self._selectionChangedCallback = None
+ self.selectionChangedCallback = None
self._coloredColumn = None
def setGlyphs(self, newGlyphs):
self.glyphs = newGlyphs
# TODO: we don't need to reallocate cells, split alloc and fill
self.updateCells()
-
+
def updateCells(self):
if self._blocked: return
self.blockSignals(True)
@@ -606,7 +600,7 @@ class SpaceTable(QTableWidget):
self.setCurrentItem(None)
self.fillGlyphs()
self.blockSignals(False)
-
+
def _cellEdited(self, row, col):
if row == 0 or col == 0: return
item = self.item(row, col).text()
@@ -635,12 +629,12 @@ class SpaceTable(QTableWidget):
if current is not None and cur == prev:
return
self.colorColumn(current if current is None else cur)
- if self._selectionChangedCallback is not None:
+ if self.selectionChangedCallback is not None:
if current is not None:
- self._selectionChangedCallback(cur - 1)
+ self.selectionChangedCallback(cur - 1)
else:
- self._selectionChangedCallback(None)
-
+ self.selectionChangedCallback(None)
+
def colorColumn(self, column):
emptyBrush = QBrush(Qt.NoBrush)
selectionColor = QColor(235, 235, 235)
@@ -671,9 +665,6 @@ class SpaceTable(QTableWidget):
if glyphIndex is not None:
self.colorColumn(glyphIndex+1)
self.blockSignals(False)
-
- def setSelectionCallback(self, selectionChangedCallback):
- self._selectionChangedCallback = selectionChangedCallback
def fillGlyphs(self):
def glyphTableWidgetItem(content, disableCell=False):
@@ -698,16 +689,8 @@ class SpaceTable(QTableWidget):
self.setItem(2, index+1, glyphTableWidgetItem(glyph.leftMargin))
self.setItem(3, index+1, glyphTableWidgetItem(glyph.rightMargin))
self.setColumnWidth(index+1, self._cellWidth)
-
+
def wheelEvent(self, event):
cur = self.horizontalScrollBar().value()
self.horizontalScrollBar().setValue(cur - event.angleDelta().y() / 120)
event.accept()
-
-if __name__ == '__main__':
- import sys
-# registerallfactories
- app = QApplication(sys.argv)
- window = Window()
- window.show()
- sys.exit(app.exec_())
diff --git a/README.md b/README.md
index 1e5ed94..13aa995 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Dependencies:
Run:
-`python -m Lib\defconQt`
+`cd Lib && python -m defconQt`
The install script is not very reliable just now, but nor is the package. Direct execution is the best
supported way at the moment. \ No newline at end of file
diff --git a/setup.py b/setup.py
index 85211e7..940dc79 100644
--- a/setup.py
+++ b/setup.py
@@ -33,14 +33,15 @@ except:
setup(name="defconQt",
- version="0.1",
+ version="0.1.0",
description="A set of Qt interface objects for working with font data.",
author="Adrien Tétar",
author_email="adri-from-59@hotmail.fr",
# url="",
- license="GNU LGPL 2.1/GNU GPL v3",
+ license="GNU LGPL 2.1/GNU GPL v3",
packages=[
"defconQt",
+ "defconQt.objects",
"defconQt.representationFactories",
],
package_dir={"":"Lib"}