aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrien Tétar2015-09-21 22:14:45 +0200
committerAdrien Tétar2015-09-21 22:14:45 +0200
commitce68fd8b05271c317d908ce4c8d73c9cbd1656ad (patch)
tree42493efb33c2a1841203a82c92e22b893bb0b8e4
parent411b0f594f6d660f13f3e1b57873a9fbda0dbd89 (diff)
downloadtrufont-ce68fd8b05271c317d908ce4c8d73c9cbd1656ad.tar.bz2
meta/defcon: check-in copy-paste, template glyph
-rw-r--r--Lib/defconQt/__main__.py4
-rw-r--r--Lib/defconQt/fontView.py10
-rw-r--r--Lib/defconQt/glyphView.py58
-rw-r--r--Lib/defconQt/objects/defcon.py162
-rw-r--r--Lib/defconQt/pens/__init__.py0
-rw-r--r--Lib/defconQt/pens/copySelectionPen.py59
-rw-r--r--setup.py1
7 files changed, 272 insertions, 22 deletions
diff --git a/Lib/defconQt/__main__.py b/Lib/defconQt/__main__.py
index 07f273c..c1e548c 100644
--- a/Lib/defconQt/__main__.py
+++ b/Lib/defconQt/__main__.py
@@ -1,4 +1,4 @@
-from defcon import Font
+from defconQt.objects.defcon import TFont
from defconQt.fontView import MainWindow
import sys
@@ -20,6 +20,6 @@ representationFactories.registerAllFactories()
app = QApplication(sys.argv)
# TODO: http://stackoverflow.com/a/21330349/2037879
app.setWindowIcon(QIcon("defconQt/resources/icon.png"))
-window = MainWindow(Font(ufoFile))
+window = MainWindow(TFont(ufoFile))
window.show()
sys.exit(app.exec_())
diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py
index ba773c3..6a603a4 100644
--- a/Lib/defconQt/fontView.py
+++ b/Lib/defconQt/fontView.py
@@ -1,10 +1,9 @@
-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.objects.defcon import CharacterSet, TFont
from defconQt.spaceCenter import MainSpaceWindow
from fontTools.agl import AGL2UV
# TODO: remove globs when things start to stabilize
@@ -80,7 +79,7 @@ class AddGlyphDialog(QDialog):
self.setLayout(layout)
@staticmethod
- def getNewGlyphNames(parent=None, currentGlyphs=None):
+ def getNewGlyphNames(parent, currentGlyphs=None):
dialog = AddGlyphDialog(currentGlyphs, parent)
result = dialog.exec_()
sortFont = False
@@ -229,7 +228,7 @@ class SortDialog(QDialog):
return 0
@staticmethod
- def getDescriptor(parent=None, sortDescriptor=None):
+ def getDescriptor(parent, sortDescriptor=None):
dialog = SortDialog(sortDescriptor, parent)
result = dialog.exec_()
if dialog.characterSetBox.isChecked():
@@ -336,7 +335,7 @@ class MainWindow(QMainWindow):
def newFile(self):
ok = self.maybeSaveBeforeExit()
if not ok: return
- self.font = Font()
+ self.font = TFont()
self.font.info.unitsPerEm = 1000
self.font.info.ascender = 750
self.font.info.descender = -250
@@ -476,6 +475,7 @@ class MainWindow(QMainWindow):
else:
glyph.lib["public.markColor"] = ",".join(str(c) for c in color.getRgbF())
+ # TODO: maybe store this in TFont
def newStandardGlyph(self, name):
self.font.newGlyph(name)
self.font[name].width = 500
diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py
index c42e4b7..cfeff3b 100644
--- a/Lib/defconQt/glyphView.py
+++ b/Lib/defconQt/glyphView.py
@@ -1,7 +1,7 @@
from enum import Enum
from math import copysign
-from defcon.objects.contour import Contour
-from defcon.objects.point import Point
+from defconQt.objects.defcon import TContour, TGlyph
+from defconQt.pens.copySelectionPen import CopySelectionPen
from fontTools.misc import bezierTools
from PyQt5.QtCore import *#QFile, QLineF, QObject, QPointF, QRectF, QSize, Qt
from PyQt5.QtGui import *#QBrush, QColor, QImage, QKeySequence, QPainter, QPainterPath, QPixmap, QPen
@@ -11,13 +11,13 @@ from PyQt5.QtWidgets import *#(QAction, QActionGroup, QApplication, QFileDialog,
from PyQt5.QtOpenGL import QGL, QGLFormat, QGLWidget
-class GotoWindow(QDialog):
+class GotoDialog(QDialog):
alphabetical = [
dict(type="alphabetical", allowPseudoUnicode=True)
]
def __init__(self, font, parent=None):
- super(GotoWindow, self).__init__(parent)
+ super(GotoDialog, self).__init__(parent)
self.setWindowModality(Qt.WindowModal)
self.setWindowTitle("Go to…")
self.font = font
@@ -79,14 +79,18 @@ class GotoWindow(QDialog):
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()
+ @staticmethod
+ def getNewGlyph(parent, font):
+ dialog = GotoDialog(font, parent)
+ result = dialog.exec_()
+ currentItem = dialog.glyphList.currentItem()
+ newGlyph = None
if currentItem is not None:
- targetGlyph = currentItem.text()
- if not targetGlyph in self.font: return
- self._view.setGlyph(self.font[targetGlyph])
- super(GotoWindow, self).accept()
+ newGlyphName = currentItem.text()
+ if newGlyphName in dialog.font:
+ newGlyph = dialog.font[newGlyphName]
+ return (newGlyph, result)
+
class MainGfxWindow(QMainWindow):
def __init__(self, glyph, parent=None):
@@ -263,6 +267,9 @@ class OffCurvePointItem(QGraphicsEllipseItem):
value.setY(0)
elif change == QGraphicsItem.ItemPositionHasChanged:
self.parentItem()._CPMoved(value)
+ # TODO: consider what to do w offCurves
+ #elif change == QGraphicsItem.ItemSelectedHasChanged:
+ # pass#self.parentItem()._CPSelChanged(value)
return value
def mousePressEvent(self, event):
@@ -432,6 +439,8 @@ class OnCurvePointItem(QGraphicsPathItem):
self._contour[pointIndex+1].x = self.pos().x()+nextPos.x()
self._contour[pointIndex+1].y = self.pos().y()+nextPos.y()
self.setShallowDirty()
+ elif change == QGraphicsItem.ItemSelectedHasChanged:
+ self._point.selected = value
return value
def setShallowDirty(self):
@@ -667,9 +676,11 @@ class GlyphScene(QGraphicsScene):
event.accept()
return
elif key == Qt.Key_J:
- glyph = self.views()[0]._glyph
- dialog = GotoWindow(glyph.getParent(), self.parent())
- dialog.exec_()
+ view = self.views()[0]
+ glyph = view._glyph
+ newGlyph, ok = GotoDialog.getNewGlyph(self.parent(), glyph.getParent())
+ if ok and newGlyph is not None:
+ view.setGlyph(newGlyph)
return
elif event.matches(QKeySequence.Undo):
if len(self._dataForUndo) > 0:
@@ -699,6 +710,23 @@ class GlyphScene(QGraphicsScene):
self.setSelectionArea(QPainterPath(), view.transform())
event.accept()
return
+ elif event.matches(QKeySequence.Copy):
+ clipboard = QApplication.clipboard()
+ mimeData = clipboard.mimeData()
+ pen = CopySelectionPen()
+ self._glyphObject.drawPoints(pen)
+ # XXX: clipboard should outlive the widget window!
+ self._clipboardObject = pen.getGlyph().serializeForUndo()
+ event.accept()
+ return
+ elif event.matches(QKeySequence.Paste):
+ if self._clipboardObject is None: return
+ pen = self._glyphObject.getPointPen()
+ pasteGlyph = TGlyph()
+ pasteGlyph.deserializeFromUndo(self._clipboardObject)
+ pasteGlyph.drawPoints(pen)
+ event.accept()
+ return
else:
sel = self.selectedItems()
if len(sel) == 1 and isinstance(sel[0], OffCurvePointItem) and \
@@ -798,7 +826,7 @@ class GlyphScene(QGraphicsScene):
lastContour.dirty = True
self._editing = True
elif not (touched and isinstance(touched, OnCurvePointItem)):
- nextC = Contour()
+ nextC = TContour()
self._glyphObject.appendContour(nextC)
nextC.addPoint((x,y), "move")
diff --git a/Lib/defconQt/objects/defcon.py b/Lib/defconQt/objects/defcon.py
index 487865a..5de0297 100644
--- a/Lib/defconQt/objects/defcon.py
+++ b/Lib/defconQt/objects/defcon.py
@@ -1,3 +1,165 @@
+from defcon import Font, Contour, Glyph, Point
+from defcon.objects.base import BaseObject
+
+class TFont(Font):
+ def __init__(self, *args, **kwargs):
+ if not "glyphClass" in kwargs:
+ kwargs["glyphClass"] = TGlyph
+ if not "glyphContourClass" in kwargs:
+ kwargs["glyphContourClass"] = TContour
+ if not "glyphPointClass" in kwargs:
+ kwargs["glyphPointClass"] = TPoint
+ super(TFont, self).__init__(*args, **kwargs)
+
+ # XXX: had to copy all of this so as to exclude template glyphs from
+ # save. It pains me that's the only thing to be done w current upstream
+ def save(self, path=None, formatVersion=None):
+ saveAs = False
+ if path is not None and path != self._path:
+ saveAs = True
+ else:
+ path = self._path
+ ## work out the format version
+ # if None is given, fallback to the one that
+ # came in when the UFO was loaded
+ if formatVersion is None and self._ufoFormatVersion is not None:
+ formatVersion = self._ufoFormatVersion
+ # otherwise fallback to 2
+ elif self._ufoFormatVersion is None:
+ formatVersion = 2
+ ## make a UFOWriter
+ ufoWriter = ufoLib.UFOWriter(path, formatVersion=formatVersion)
+ ## save objects
+ saveInfo = False
+ saveKerning = False
+ saveGroups = False
+ saveFeatures = False
+ ## lib should always be saved
+ saveLib = True
+ # if in a save as, save all objects
+ if saveAs:
+ saveInfo = True
+ saveKerning = True
+ saveGroups = True
+ saveFeatures = True
+ ## if changing ufo format versions, save all objects
+ if self._ufoFormatVersion != formatVersion:
+ saveInfo = True
+ saveKerning = True
+ saveGroups = True
+ saveFeatures = True
+ # save info, kerning and features if they are dirty
+ if self._info is not None and self._info.dirty:
+ saveInfo = True
+ if self._kerning is not None and self._kerning.dirty:
+ saveKerning = True
+ if self._features is not None and self._features.dirty:
+ saveFeatures = True
+ # always save groups and lib if they are loaded
+ # as they contain sub-objects that may not notify
+ # the main object about changes.
+ if self._groups is not None:
+ saveGroups = True
+ if self._lib is not None:
+ saveLib = True
+ # save objects as needed
+ if saveInfo:
+ ufoWriter.writeInfo(self.info)
+ self._stampInfoDataState()
+ self.info.dirty = False
+ if saveKerning:
+ ufoWriter.writeKerning(self.kerning)
+ self._stampKerningDataState()
+ self.kerning.dirty = False
+ if saveGroups:
+ ufoWriter.writeGroups(self.groups)
+ self._stampGroupsDataState()
+ if saveFeatures and formatVersion > 1:
+ ufoWriter.writeFeatures(self.features.text)
+ self._stampFeaturesDataState()
+ if saveLib:
+ # if making format version 1, do some
+ # temporary down conversion before
+ # passing the lib to the writer
+ libCopy = dict(self.lib)
+ if formatVersion == 1:
+ self._convertToFormatVersion1RoboFabData(libCopy)
+ ufoWriter.writeLib(libCopy)
+ self._stampLibDataState()
+ ## save glyphs
+ # for a save as operation, load all the glyphs
+ # and mark them as dirty.
+ if saveAs:
+ for glyph in self:
+ glyph.dirty = True
+ glyphSet = ufoWriter.getGlyphSet()
+ for glyphName, glyphObject in self._glyphs.items():
+ if glyphObject.template: continue
+ if glyphObject.dirty:
+ glyphSet.writeGlyph(glyphName, glyphObject, glyphObject.drawPoints)
+ self._stampGlyphDataState(glyphObject)
+ # remove deleted glyphs
+ if not saveAs and self._scheduledForDeletion:
+ for glyphName in self._scheduledForDeletion:
+ if glyphName in glyphSet:
+ glyphSet.deleteGlyph(glyphName)
+ glyphSet.writeContents()
+ self._glyphSet = glyphSet
+ self._scheduledForDeletion = []
+ self._path = path
+ self._ufoFormatVersion = formatVersion
+ self.dirty = False
+
+class TGlyph(Glyph):
+ def __init__(self, *args, **kwargs):
+ super(TGlyph, self).__init__(*args, **kwargs)
+ self._template = False
+
+ def _get_template(self):
+ return self._template
+
+ def _set_template(self, value):
+ self._template = value
+
+ template = property(_get_template, _set_template, doc="A boolean indicating whether the glyph is a template glyph.")
+
+ def _set_dirty(self, value):
+ BaseObject._set_dirty(self, value)
+ if value:
+ self.template = False
+
+ dirty = property(BaseObject._get_dirty, _set_dirty)
+
+class TContour(Contour):
+ def __init__(self, pointClass=None):
+ if pointClass is None:
+ pointClass = TPoint
+ super(TContour, self).__init__(pointClass)
+
+ def drawPoints(self, pointPen):
+ """
+ Draw the contour with **pointPen**.
+ """
+ pointPen.beginPath()
+ for point in self._points:
+ pointPen.addPoint((point.x, point.y), segmentType=point.segmentType, smooth=point.smooth, name=point.name, selected=point.selected)
+ pointPen.endPath()
+
+class TPoint(Point):
+ __slots__ = ["_selected"]
+
+ def __init__(self, pt, segmentType=None, smooth=False, name=None, selected=False):
+ super(TPoint, self).__init__(pt, segmentType, smooth, name)
+ self._selected = selected
+
+ def _get_selected(self):
+ return self._selected
+
+ def _set_selected(self, value):
+ self._selected = value
+
+ selected = property(_get_selected, _set_selected, doc="A boolean indicating the selected state of the point.")
+
class CharacterSet(object):
__slots__ = ["_name", "_glyphNames"]
diff --git a/Lib/defconQt/pens/__init__.py b/Lib/defconQt/pens/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/defconQt/pens/__init__.py
diff --git a/Lib/defconQt/pens/copySelectionPen.py b/Lib/defconQt/pens/copySelectionPen.py
new file mode 100644
index 0000000..53d917b
--- /dev/null
+++ b/Lib/defconQt/pens/copySelectionPen.py
@@ -0,0 +1,59 @@
+from defcon import Glyph
+from robofab.pens.pointPen import AbstractPointPen
+
+class CopySelectionPen(AbstractPointPen):
+ def __init__(self, glyph=None):
+ if glyph is None:
+ glyph = Glyph()
+ self._glyph = glyph
+ self._contour = None
+ self._havePoint = False
+ self._originalContourIsOpen = False
+
+ def beginPath(self):
+ self._contour = self._glyph.contourClass(pointClass=self._glyph.pointClass)
+
+ def endPath(self, keepOpened=False):
+ if self._havePoint:
+ self.elideOrphanOffCurves(True)
+ if self._originalContourIsOpen or keepOpened:
+ self._contour[0].segmentType = "move"
+ self._contour.dirty = False
+ self._glyph.appendContour(self._contour)
+ self._contour = None
+ self._havePoint = False
+ self._originalContourIsOpen = False
+
+ def addPoint(self, pt, segmentType=None, smooth=False, name=None, selected=False, **kwargs):
+ if segmentType == "move":
+ self._originalContourIsOpen = True
+ if segmentType is None or selected:
+ if selected:
+ self._havePoint = True
+ self._contour.addPoint(pt, segmentType, smooth, name)
+ else:
+ self.elideOrphanOffCurves(False)
+ if self._havePoint:
+ # We started drawing a path and we have a gap in it. Create
+ # a new contour (and leave this one opened).
+ self.endPath(True)
+ self.beginPath()
+
+ def addComponent(self, baseGlyphName, transformation):
+ pass # XXX
+
+ def elideOrphanOffCurves(self, arrivedAtBoundary):
+ # onCurves that aren't selected and preceding offCurves if any are
+ # elided
+ for _ in range(2):
+ if len(self._contour):
+ if len(self._contour) > 1 and arrivedAtBoundary and \
+ self._contour[0].segmentType == "curve":
+ # We're at the end of drawing and the offCurves lead to begin
+ # onCurve. Let them in.
+ pass
+ elif self._contour[-1].segmentType is None:
+ self._contour.removePoint(self._contour[-1])
+
+ def getGlyph(self):
+ return self._glyph
diff --git a/setup.py b/setup.py
index 940dc79..c0f3249 100644
--- a/setup.py
+++ b/setup.py
@@ -42,6 +42,7 @@ setup(name="defconQt",
packages=[
"defconQt",
"defconQt.objects",
+ "defconQt.pens",
"defconQt.representationFactories",
],
package_dir={"":"Lib"}