aboutsummaryrefslogtreecommitdiffstats
path: root/Lib/defconQt/glyphView.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/defconQt/glyphView.py')
-rw-r--r--Lib/defconQt/glyphView.py269
1 files changed, 164 insertions, 105 deletions
diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py
index c2aeafe..811dd14 100644
--- a/Lib/defconQt/glyphView.py
+++ b/Lib/defconQt/glyphView.py
@@ -6,6 +6,7 @@ import pickle
from defcon import Anchor, Component
from defconQt import icons_db # noqa
from defconQt.objects.defcon import TContour, TGlyph
+from defconQt.objects.sizeGripItem import ResizeHandleItem, SizeGripItem
from defconQt.pens.copySelectionPen import CopySelectionPen
from defconQt.util import platformSpecific
from fontTools.misc import bezierTools
@@ -16,7 +17,7 @@ from PyQt5.QtGui import (
from PyQt5.QtWidgets import (
QAction, QActionGroup, QApplication, QColorDialog, QComboBox, QDialog,
QDialogButtonBox, QGraphicsEllipseItem, QGraphicsItem, QGraphicsLineItem,
- QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsRectItem, QGraphicsScene,
+ QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsScene,
QGraphicsSimpleTextItem, QGraphicsView, QGridLayout, QLabel, QLineEdit,
QListWidget, QMainWindow, QMenu, QRadioButton, QSizePolicy, QStyle,
QStyleOptionGraphicsItem, QToolBar, QWidget)
@@ -197,6 +198,65 @@ class AddLayerDialog(QDialog):
return (name, result)
+class LayerActionsDialog(QDialog):
+
+ def __init__(self, currentGlyph, parent=None):
+ super().__init__(parent)
+ self.setWindowModality(Qt.WindowModal)
+ self.setWindowTitle("Layer actions…")
+ self._workableLayers = []
+ for layer in currentGlyph.layerSet:
+ if layer != currentGlyph.layer:
+ self._workableLayers.append(layer)
+
+ layout = QGridLayout(self)
+
+ copyBox = QRadioButton("Copy", self)
+ moveBox = QRadioButton("Move", self)
+ swapBox = QRadioButton("Swap", self)
+ self.otherCheckBoxes = (moveBox, swapBox)
+ copyBox.setChecked(True)
+
+ self.layersList = QListWidget(self)
+ self.layersList.addItems(
+ layer.name for layer in self._workableLayers)
+ if self.layersList.count():
+ self.layersList.setCurrentRow(0)
+ self.layersList.itemDoubleClicked.connect(self.accept)
+
+ buttonBox = QDialogButtonBox(
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ buttonBox.accepted.connect(self.accept)
+ buttonBox.rejected.connect(self.reject)
+
+ l = 0
+ layout.addWidget(copyBox, l, 0, 1, 2)
+ layout.addWidget(moveBox, l, 2, 1, 2)
+ layout.addWidget(swapBox, l, 4, 1, 2)
+ l += 1
+ layout.addWidget(self.layersList, l, 0, 1, 6)
+ l += 1
+ layout.addWidget(buttonBox, l, 0, 1, 6)
+ self.setLayout(layout)
+
+ @classmethod
+ def getLayerAndAction(cls, parent, currentGlyph):
+ dialog = cls(currentGlyph, parent)
+ result = dialog.exec_()
+ currentItem = dialog.layersList.currentItem()
+ newLayer = None
+ if currentItem is not None:
+ newLayerName = currentItem.text()
+ for layer in dialog._workableLayers:
+ if layer.name == newLayerName:
+ newLayer = layer
+ action = "Copy"
+ for checkBox in dialog.otherCheckBoxes:
+ if checkBox.isChecked():
+ action = checkBox.text()
+ return (newLayer, action, result)
+
+
class GenericSettings(object):
def __init__(self, title, parent, callback):
@@ -307,7 +367,8 @@ class MainGfxWindow(QMainWindow):
menuBar.addMenu(fileMenu)
glyphMenu = QMenu("&Glyph", self)
- glyphMenu.addAction("&Go to…", self.changeGlyph, "G")
+ glyphMenu.addAction("&Go To…", self.changeGlyph, "G")
+ glyphMenu.addAction("&Layer Actions…", self.layerActions, "L")
menuBar.addMenu(glyphMenu)
self._displaySettings = DisplayStyleSettings(
@@ -364,6 +425,10 @@ class MainGfxWindow(QMainWindow):
self.setWindowTitle(glyph.name, glyph.getParent())
self.adjustSize()
+ def layerActions(self):
+ if self.view is not None:
+ self.view.layerActions()
+
def _changeGlyph(self, glyph):
oldView = self.view
# Preserve the selected layer (by setting the glyph from that layer)
@@ -626,7 +691,7 @@ class OffCurvePointItem(QGraphicsEllipseItem):
self._needsUngrab = False
def delete(self):
- self.parentItem()._CPDeleted()
+ self.parentItem()._CPDeleted(self)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange:
@@ -646,10 +711,9 @@ class OffCurvePointItem(QGraphicsEllipseItem):
else:
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)
+ self.parentItem()._CPMoved(self, value)
+ elif change == QGraphicsItem.ItemSelectedHasChanged:
+ self.parentItem()._CPSelected(self, value)
return value
def mousePressEvent(self, event):
@@ -775,26 +839,33 @@ class OnCurvePointItem(QGraphicsPathItem):
index += 1
return index % len(self._contour.segments)
- def _CPDeleted(self):
+ def _CPDeleted(self, item):
+ # XXX: is this sufficient guard?
+ if self.isSelected():
+ return
pointIndex = self.getPointIndex()
children = self.childItems()
- selected = 1
- if not (children[1].isVisible() and children[1].isSelected()):
- selected = 3
+ if item == children[1]:
+ delta = -1
+ segmentOn = 0
+ else:
+ delta = 1
+ segmentOn = 3
- firstSibling = self._contour[pointIndex + selected - 2]
- secondSibling = self._contour[pointIndex + (selected - 2) * 2]
+ firstSibling = self._contour.getPoint(pointIndex + delta)
+ secondSibling = self._contour.getPoint(pointIndex + delta * 2)
if (firstSibling.segmentType is None and
secondSibling.segmentType is None):
# we have two offCurves, wipe them
+ self._contour.getPoint(pointIndex + segmentOn).segmentType = "line"
self._contour.removePoint(firstSibling)
self._contour.removePoint(secondSibling)
- def _CPMoved(self, newValue):
+ def _CPMoved(self, item, newValue):
pointIndex = self.getPointIndex()
children = self.childItems()
# nodes are stored after lines (for stacking order)
- if children[1].isSelected():
+ if item == children[1]:
selected = 1
propagate = 3
else:
@@ -806,8 +877,8 @@ class OnCurvePointItem(QGraphicsPathItem):
if not len(children) > 4:
elemIndex = pointIndex - 2 + selected
- self._contour[elemIndex].x = self.pos().x() + newValue.x()
- self._contour[elemIndex].y = self.pos().y() + newValue.y()
+ self._contour.getPoint(elemIndex).x = self.pos().x() + newValue.x()
+ self._contour.getPoint(elemIndex).y = self.pos().y() + newValue.y()
if not (self._isSmooth and children[propagate].isVisible()):
self.setShallowDirty()
return
@@ -829,11 +900,21 @@ class OnCurvePointItem(QGraphicsPathItem):
children[propagate].setFlag(QGraphicsItem.ItemSendsGeometryChanges)
children[propagate - 1].setLine(line.x1(),
line.y1(), tmpLine.x2(), tmpLine.y2())
- propagateInContour = pointIndex - 2 + propagate
- self._contour[propagateInContour].x = self.pos().x() + tmpLine.x2()
- self._contour[propagateInContour].y = self.pos().y() + tmpLine.y2()
+ propagateIn = pointIndex - 2 + propagate
+ self._contour.getPoint(propagateIn).x = self.pos().x() + tmpLine.x2()
+ self._contour.getPoint(propagateIn).y = self.pos().y() + tmpLine.y2()
self.setShallowDirty()
+ def _CPSelected(self, item, value):
+ pointIndex = self.getPointIndex()
+ children = self.childItems()
+ if item == children[1]:
+ delta = -1
+ else:
+ delta = 1
+
+ self._contour[pointIndex + delta].selected = value
+
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange:
if self.scene()._integerPlane:
@@ -848,12 +929,14 @@ class OnCurvePointItem(QGraphicsPathItem):
children = self.childItems()
if children[1].isVisible():
prevPos = children[1].pos()
- self._contour[pointIndex - 1].x = self.pos().x() + prevPos.x()
- self._contour[pointIndex - 1].y = self.pos().y() + prevPos.y()
+ point = self._contour.getPoint(pointIndex - 1)
+ point.x = self.pos().x() + prevPos.x()
+ point.y = self.pos().y() + prevPos.y()
if children[3].isVisible():
nextPos = children[3].pos()
- self._contour[pointIndex + 1].x = self.pos().x() + nextPos.x()
- self._contour[pointIndex + 1].y = self.pos().y() + nextPos.y()
+ point = self._contour.getPoint(pointIndex + 1)
+ point.x = self.pos().x() + nextPos.x()
+ point.y = self.pos().y() + nextPos.y()
self.setShallowDirty()
elif change == QGraphicsItem.ItemSelectedHasChanged:
self._point.selected = value
@@ -886,11 +969,11 @@ class OnCurvePointItem(QGraphicsPathItem):
scene = self.scene()
scene._blocked = True
# if we have line segment, insert offCurve points
- insertIndex = (ptIndex + (i - 1) // 2) % len(self._contour)
- if self._contour[insertIndex].segmentType == "line":
- nextToCP = self._contour[(ptIndex - 2 + i) % len(self._contour)]
+ insertIndex = ptIndex + (i - 1) // 2
+ if self._contour.getPoint(insertIndex).segmentType == "line":
+ nextToCP = self._contour.getPoint(ptIndex - 2 + i)
assert(nextToCP.segmentType is not None)
- self._contour[insertIndex].segmentType = "curve"
+ self._contour.getPoint(insertIndex).segmentType = "curve"
if i == 1:
first, second = (
self._point.x, self._point.y), (nextToCP.x, nextToCP.y)
@@ -1104,86 +1187,32 @@ class VGuidelinesTextItem(QGraphicsSimpleTextItem):
self.setFont(font)
-class ResizeHandleItem(QGraphicsRectItem):
-
- def __init__(self, parent=None):
- super(QGraphicsRectItem, self).__init__(parent)
- self.setPointPath()
- self.setBrush(QBrush(QColor(60, 60, 60)))
- self.setPen(QPen(Qt.NoPen))
- self.setFlag(QGraphicsItem.ItemIgnoresParentOpacity)
- 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()
- if scene is not None:
- scale = scene.getViewScale()
- else:
- scale = 1
- if scale > 4:
- scale = 4
- self.prepareGeometryChange()
- self.setRect(-onHalf / scale, -onHalf / scale,
- onWidth / scale, onHeight / scale)
-
-
class PixmapItem(QGraphicsPixmapItem):
- def __init__(self, x, y, pixmap, parent=None):
+ def __init__(self, x, y, pixmap, scale, parent=None):
super(QGraphicsPixmapItem, self).__init__(pixmap, parent)
- self.setPos(x, y)
- self.setFlag(QGraphicsItem.ItemIsMovable)
+ self._pixmap = pixmap
+ self.setOffset(x, -y)
self.setFlag(QGraphicsItem.ItemIsSelectable)
+ self.setShapeMode(QGraphicsPixmapItem.BoundingRectShape)
self.setTransform(QTransform().fromScale(1, -1))
- self.setOpacity(.5)
+ self.setOpacity(.6)
self.setZValue(-1)
-
- rect = self.boundingRect()
- self._rWidth = rect.width()
- self._rHeight = rect.height()
- handle = ResizeHandleItem(self)
- handle.setVisible(False)
-
- def _pixmapGeometryChanged(self, event):
- modifiers = event.modifiers()
- pos = event.scenePos()
- if modifiers & Qt.ControlModifier:
- # rotate
- refLine = QLineF(self.x(), self.y(), self.x() +
- self._rWidth, self.y() - self._rHeight)
- curLine = QLineF(self.x(), self.y(), pos.x(), pos.y())
- self.setRotation(refLine.angleTo(curLine))
- else:
- # scale
- dy = (pos.y() - self.y()) / self._rHeight
- if modifiers & Qt.ShiftModifier:
- # keep original aspect ratio
- dx = -dy
- else:
- dx = (pos.x() - self.x()) / self._rWidth
- self.setTransform(QTransform().fromScale(dx, dy))
- event.accept()
+ sizeGripItem = SizeGripItem(scale, self)
+ sizeGripItem.setVisible(False)
+
+ def setRect(self, rect):
+ dTopLeft = rect.topLeft() - self.pos()
+ if not dTopLeft.isNull():
+ self.setOffset(dTopLeft)
+ self.setPixmap(self._pixmap.scaled(
+ rect.size().toSize()
+ ))
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
- children = self.childItems()
- if not children[0].isUnderMouse():
- children[0].setVisible(value)
+ sizeGripItem = self.childItems()[0]
+ sizeGripItem.setVisible(value)
return value
@@ -1247,8 +1276,8 @@ class GlyphScene(QGraphicsScene):
else:
return
pos = event.scenePos()
- newPix = PixmapItem(pos.x(), pos.y(), dragPix)
- self._addRegisterItem(newPix)
+ pixmapItem = PixmapItem(pos.x(), pos.y(), dragPix, self.getViewScale())
+ self._addRegisterItem(pixmapItem)
event.acceptProposedAction()
def getItemForPoint(self, point):
@@ -1289,8 +1318,6 @@ class GlyphScene(QGraphicsScene):
elif isinstance(item, (AnchorItem, ComponentItem,
OffCurvePointItem)):
item.delete()
- elif isinstance(item, PixmapItem):
- self.removeItem(item)
self._blocked = False
self._glyphObject.dirty = True
event.accept()
@@ -1776,6 +1803,8 @@ class GlyphScene(QGraphicsScene):
for contour in glyph:
segments = contour.segments
for index, seg in enumerate(segments):
+ if seg[-1].segmentType == "move":
+ continue
prev = segments[index - 1][-1]
if len(seg) == 3:
i = self.computeIntersections(
@@ -1805,6 +1834,9 @@ class GlyphScene(QGraphicsScene):
for dot in self._knifeDots:
self.removeItem(dot)
self._knifeDots = []
+ # no-move clicks
+ if self._cachedIntersections is None:
+ return
# reverse so as to not invalidate our cached segment indexes
for loc, ts in reversed(list(self._cachedIntersections.items())):
contour, index = loc
@@ -2181,14 +2213,13 @@ class GlyphView(QGraphicsView):
def updateActiveLayerPath(self):
self.updateLayerPath(
self._layer, representationKey="defconQt.NoComponentsQPainterPath")
+ self.addStartPoints()
def updateLayerPath(self, layer,
representationKey="defconQt.QPainterPath"):
glyph = layer[self._name]
- # scene = self.scene() # unused
path = glyph.getRepresentation(representationKey)
self._sceneItems[layer].setPath(path)
- self.addStartPoints()
def _getSceneItems(self, key, clear=False):
items = self._sceneItems.get(key, None)
@@ -2317,6 +2348,34 @@ class GlyphView(QGraphicsView):
component.baseGlyph = newGlyph.name
self._glyph.appendComponent(component)
+ def layerActions(self):
+ newLayer, action, ok = LayerActionsDialog.getLayerAndAction(
+ self, self._glyph)
+ if ok and newLayer is not None:
+ # TODO: whole glyph for now, but consider selection too
+ if not self._glyph.name in newLayer:
+ newLayer.newGlyph(self._glyph.name)
+ otherGlyph = newLayer[self._glyph.name]
+ otherGlyph.disableNotifications()
+ if action == "Swap":
+ tempGlyph = TGlyph()
+ otherGlyph.drawPoints(tempGlyph.getPointPen())
+ tempGlyph.width = otherGlyph.width
+ otherGlyph.clearContours()
+ self._glyph.drawPoints(otherGlyph.getPointPen())
+ otherGlyph.width = self._glyph.width
+ if action != "Copy":
+ self._glyph.disableNotifications()
+ self._glyph.clearContours()
+ # XXX: we shouldn't have to do this manually but it seems there
+ # is a timing problem
+ self._glyph.destroyAllRepresentations()
+ if action == "Swap":
+ tempGlyph.drawPoints(self._glyph.getPointPen())
+ self._glyph.width = tempGlyph.width
+ self._glyph.enableNotifications()
+ otherGlyph.enableNotifications()
+
def _makeLayerGlyph(self, layer):
name = self._name
glyph = layer.newGlyph(name)