diff options
| author | Lasse Fister | 2015-10-15 17:26:10 +0200 |
|---|---|---|
| committer | Adrien Tétar | 2015-10-16 21:07:01 +0200 |
| commit | 3e11f021e7ee96c006c476e5043e9261a00a5eea (patch) | |
| tree | 2c35b92147044001fc4042f9f1ca6fb4e08a4adc /Lib/defconQt/glyphView.py | |
| parent | 700df7380b08f84fe7a593c204a5e787eb3aee12 (diff) | |
| download | trufont-3e11f021e7ee96c006c476e5043e9261a00a5eea.tar.bz2 | |
enhanced layer support in glyphView
font wider layer control widget
cleaning up after first round of review #17
fix 2 typos
Place LayerSetWidget controls inside of InspectorWindow
default color swatch is a red diagonal with white background
LayerSetWidget to LayerSetList, changed the superclass as well
remove unused import
Diffstat (limited to 'Lib/defconQt/glyphView.py')
| -rw-r--r-- | Lib/defconQt/glyphView.py | 722 |
1 files changed, 592 insertions, 130 deletions
diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py index c68f188..bd5779f 100644 --- a/Lib/defconQt/glyphView.py +++ b/Lib/defconQt/glyphView.py @@ -1,5 +1,6 @@ from enum import Enum from math import copysign +from functools import partial import pickle from defcon import Anchor, Component from defconQt import icons_db @@ -8,7 +9,7 @@ from defconQt.pens.copySelectionPen import CopySelectionPen from defconQt.util import platformSpecific 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 +from PyQt5.QtGui import *#QBrush, QColor, QImage, QKeySequence, QPainter, QPainterPath, QPixmap, QPen, QColorDialog from PyQt5.QtWidgets import *#(QAction, QActionGroup, QApplication, QFileDialog, #QGraphicsItem, QGraphicsEllipseItem, QGraphicsLineItem, QGraphicsPathItem, QGraphicsRectItem, QGraphicsScene, QGraphicsView, #QMainWindow, QMenu, QMessageBox, QStyle, QStyleOptionGraphicsItem, QWidget) @@ -170,13 +171,116 @@ class AddLayerDialog(QDialog): name = dialog.layerNameEdit.text() return (name, result) + +class GenericSettings(object): + def __init__(self, title, parent, callback): + #super().__init__() + self._menuWidget = QMenu(title, parent) + self._actions = {} + self._callback = callback + for key, item in self._items: + self._addAction(key, *item) + + if(self._presets): + self._menuWidget.addSeparator() + for i, presets in enumerate(self._presets): + self._addPresetAction(i, *presets) + + _items = {} + _presets = tuple() + + @property + def menuWidget(self): + return self._menuWidget + + def _addAction(self, key, label, checked): + action = self._menuWidget.addAction(label, partial(self._callback, key)) + action.setCheckable(True) + action.setChecked(checked) + self._actions[key] = action + + def _addPresetAction(self, index, label, presets, **args): + self._menuWidget.addAction(label, partial(self._setPreset, index)) + + def _setPreset(self, index): + _, data = self._presets[index] + for key, value in data.items(): + action = self._actions[key] + action.blockSignals(True) + action.setChecked(value) + action.blockSignals(False) + self._callback() + + def __setattr__(self, name, value): + if name.startswith('_'): + self.__dict__[name] = value + return + raise AttributeError('It\'s not allowed to set attributes here.', name) + + def __getattr__(self, name): + if name.startswith('_'): + raise AttributeError(name) + if name not in self._actions: + raise AttributeError(name) + return self._actions[name].isChecked() + + + +class DisplayStyleSettings(GenericSettings): + _presets = ( + ('TruFont Default', dict( + activeLayerOnTop = True + , activeLayerFilled = True + , otherLayersFilled = False + , activeLayerUseLayerColor = False + , otherLayerUseLayerColor = True + , drawOtherLayers = True + )) + , ('Layer Fonts', dict( + activeLayerOnTop = False + , activeLayerFilled = True + , otherLayersFilled = True + , activeLayerUseLayerColor = True + , otherLayerUseLayerColor = True + , drawOtherLayers = True + )) + ) + + _items = ( + ('activeLayerOnTop', ('Active Layer on Top', True)) + , ('activeLayerFilled', ('Active Layer Filled', True)) + , ('activeLayerUseLayerColor', ('Active Layer use Custom Color', False)) + , ('otherLayersFilled', ('Other Layers Filled', False)) + , ('otherLayerUseLayerColor', ('Other Layers use Custom Color', True)) + , ('drawOtherLayers', ('Show Other Layers', True)) + ) + + class MainGfxWindow(QMainWindow): def __init__(self, glyph, parent=None): super(MainGfxWindow, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_KeyCompression) - self.view = GlyphView(glyph, self) + self.view = None + + self._layerSet = layerSet = glyph.layerSet + + self._layerSetNotifications = [] + + layerSet.addObserver(self, '_layerSetLayerAdded', 'LayerSet.LayerAdded') + layerSet.addObserver(self, '_layerSetLayerDeleted', 'LayerSet.LayerDeleted') + self._layerSetNotifications.append('LayerSet.LayerAdded') + self._layerSetNotifications.append('LayerSet.LayerDeleted') + + + for event in ('LayerSet.LayerChanged', 'LayerSet.DefaultLayerChanged' + , 'LayerSet.LayerOrderChanged'): + self._layerSetNotifications.append(event) + layerSet.addObserver(self, '_layerSetEvents', event) + + + menuBar = self.menuBar() fileMenu = QMenu("&File", self) @@ -187,27 +291,39 @@ class MainGfxWindow(QMainWindow): glyphMenu.addAction("&Jump", self.changeGlyph, "J") menuBar.addMenu(glyphMenu) - toolBar = QToolBar(self) + + self._displaySettings = DisplayStyleSettings("&Display", self, self._displayChanged) + menuBar.addMenu(self._displaySettings.menuWidget) + + self._toolBar = toolBar = QToolBar(self) toolBar.setMovable(False) toolBar.setContentsMargins(2, 0, 2, 0) - selectionToolButton = toolBar.addAction("Selection", self.view.setSceneSelection) + selectionToolButton = toolBar.addAction("Selection", self._redirect('view', 'setSceneSelection')) selectionToolButton.setCheckable(True) selectionToolButton.setChecked(True) selectionToolButton.setIcon(QIcon(":/resources/cursor.svg")) - penToolButton = toolBar.addAction("Pen", self.view.setSceneDrawing) + penToolButton = toolBar.addAction("Pen", self._redirect('view', 'setSceneDrawing')) penToolButton.setCheckable(True) penToolButton.setIcon(QIcon(":/resources/curve.svg")) - rulerToolButton = toolBar.addAction("Ruler", self.view.setSceneRuler) + rulerToolButton = toolBar.addAction("Ruler", self._redirect('view', 'setSceneRuler')) rulerToolButton.setCheckable(True) rulerToolButton.setIcon(QIcon(":/resources/ruler.svg")) - knifeToolButton = toolBar.addAction("Knife", self.view.setSceneKnife) + knifeToolButton = toolBar.addAction("Knife", self._redirect('view', 'setSceneKnife')) knifeToolButton.setCheckable(True) knifeToolButton.setIcon(QIcon(":/resources/cut.svg")) + # http://www.setnode.com/blog/right-aligning-a-button-in-a-qtoolbar/ spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolBar.addWidget(spacer) - toolBar.addWidget(self.view.currentLayerBox()) + self._currentLayerBox = QComboBox(self) + self._currentLayerBox.currentIndexChanged.connect(self._changeLayerHandler) + toolBar.addWidget(self._currentLayerBox) + + self._layerColorButton = toolBar.addAction( + "Layer Color (shift + click to remove the color)", + self._chooseLayerColor) + toolsGroup = QActionGroup(self) toolsGroup.addAction(selectionToolButton) toolsGroup.addAction(penToolButton) @@ -217,21 +333,177 @@ class MainGfxWindow(QMainWindow): self.setContextMenuPolicy(Qt.ActionsContextMenu) createAnchorAction = QAction("Add Anchor…", self) - createAnchorAction.triggered.connect(self.view.createAnchor) + createAnchorAction.triggered.connect(self._redirect('view', 'createAnchor')) self.addAction(createAnchorAction) createComponentAction = QAction("Add Component…", self) - createComponentAction.triggered.connect(self.view.createComponent) + createComponentAction.triggered.connect(self._redirect('view', 'createComponent')) self.addAction(createComponentAction) - self.setCentralWidget(self.view) + for layer in self._layerSet: + self._listenToLayer(layer) + + self._changeGlyph(glyph) + self._updateComboBox() + self.setWindowTitle(glyph.name, glyph.getParent()) self.adjustSize() + def _changeGlyph(self, glyph): + oldView = self.view + if oldView: + # Preserve the selected layer (by setting the glyph from that layer) + # Todo: If that layer is already in the glyph, it would be a little bit + # harder to create it here and may be better or worse. Worse becaue + # we'd alter the data without being explicitly asked to do so. + # Ask someone who does UX. + if oldView.layer is not glyph.layer and glyph.name in oldView.layer: + glyph = oldView.layer[glyph.name] + oldView.hide() + oldView.deleteLater() + + self.view = GlyphView(glyph, self._displaySettings, self) + # Preserve the zoom level of the oldView. + # TODO: is there more state that we need to carry over to the next + # GlyphView? If yes, we should make an interface for a + # predecessor -> successor transformation + if oldView: + self.view.setTransform(oldView.transform()) + + self._setlayerColorButtonColor() + + app = QApplication.instance() + app.setCurrentGlyph(glyph) + self.setWindowTitle(glyph.name, glyph.getParent()) + + # switch + self.setCentralWidget(self.view) + + def _executeRemoteCommand(self, targetName, commandName, *args, **kwds): + """ + Execute a method named `commandName` on the attribute named `targetName`. + This strongly suggest that there is always a known interface at self.{targetName} + See MainGfxWindow._redirect + """ + target = getattr(self, targetName) + command = getattr(target, commandName) + command(*args, **kwds) + + def _redirect(self, target, command): + """ + This creates an indirection to permanently connect an event to a + property that may (will) change its identity. + """ + return partial(self._executeRemoteCommand, target, command) + + def _layerSetEvents(self, notification): + layerSet = notification.object + assert layerSet is self._layerSet + self._updateComboBox() + + + def _layerSetLayerDeleted(self, notification): + self._layerSetEvents(notification) + self._changeLayerHandler(0) + + def _layerSetLayerAdded(self, notification): + self._layerSetEvents(notification) + layerName = notification.data['name'] + layer = self._layerSet[layerName] + self.view.layerAdded(layer) + self._listenToLayer(layer) + + def _listenToLayer(self, layer, remove=False): + if remove: + layer.removeObserver(self, 'Layer.ColorChanged') + else: + layer.addObserver(self, '_layerColorChange', 'Layer.ColorChanged') + + def _chooseLayerColor(self): + if QApplication.keyboardModifiers() & Qt.ShiftModifier: + # reset to default color, i.e. delete the layer color + color = None + else: + startColor = self.view.getLayerColor() or QColor('limegreen') + qColor = QColorDialog.getColor(startColor, self, options=QColorDialog.ShowAlphaChannel) + if not qColor.isValid(): + # cancelled + return + color = qColor.getRgbF() + # will trigger Layer.ColorChanged + self.view.layer.color = color + + def _layerColorChange(self, notification): + layer = notification.object + if layer is self.view.layer: + self._setlayerColorButtonColor() + + def _setlayerColorButtonColor(self): + color = self.view.getLayerColor() + if color is None: + icon = QIcon(":/resources/defaultColor.svg") + else: + # set the layer color to the button + pixmap = QPixmap(100, 100) + pixmap.fill(color) + icon = QIcon(pixmap) + self._layerColorButton.setIcon(icon) + + def _updateComboBox(self): + comboBox = self._currentLayerBox + comboBox.blockSignals(True) + comboBox.clear() + for layer in self._layerSet: + comboBox.addItem(layer.name, layer) + if layer == self.view.layer: + comboBox.setCurrentText(layer.name) + continue + comboBox.addItem("New layer...", None) + comboBox.blockSignals(False) + + def _setComboboxIndex(self, index): + comboBox = self._currentLayerBox + comboBox.blockSignals(True) + comboBox.setCurrentIndex(index) + comboBox.blockSignals(False) + + def _makeNewLayerViaDialog(self): + newLayerName, ok = AddLayerDialog.getNewLayerName(self) + if ok: + # this should cause self._layerSetLayerAdded to be executed + self._layerSet.newLayer(newLayerName) + return self._layerSet[newLayerName] + else: + return None + + def _changeLayerHandler(self, newLayerIndex): + comboBox = self._currentLayerBox + layer = comboBox.itemData(newLayerIndex) + if layer is None: + layer = self._makeNewLayerViaDialog() + if layer is None: + # restore comboBox to active index + index = self._layerSet.layerOrder.index(self.view.layer.name) + self._setComboboxIndex(index) + return + + self.view.changeCurrentLayer(layer) + self._updateComboBox() + self._setlayerColorButtonColor() + + app = QApplication.instance() + # setting the layer-glyph here + app.setCurrentGlyph(self.view._glyph) + def changeGlyph(self): glyph = self.view._glyph newGlyph, ok = GotoDialog.getNewGlyph(self, glyph) if ok and newGlyph is not None: - self.view.setGlyph(newGlyph) + self._changeGlyph(newGlyph) + self._updateComboBox() + + def _displayChanged(self, *args): + # redraw the view + self.view.redraw() def event(self, event): if event.type() == QEvent.WindowActivate: @@ -240,11 +512,15 @@ class MainGfxWindow(QMainWindow): return super(MainGfxWindow, self).event(event) def closeEvent(self, event): - self.view._glyph.removeObserver(self, "Glyph.Changed") - event.accept() + for name in self._layerSetNotifications: + self._layerSet.removeObserver(self, name) - def _glyphChanged(self, notification): - self.view._glyphChanged(notification) + for layer in self._layerSet: + self._listenToLayer(layer, remove=True) + + self.view = None + + event.accept() def setWindowTitle(self, title, font=None): if font is not None: title = "%s – %s %s" % (title, font.info.familyName, font.info.styleName) @@ -677,7 +953,6 @@ class ComponentItem(QGraphicsPathItem): super(ComponentItem, self).__init__(path, parent) self._component = component self.setTransform(QTransform(*component.transformation)) - self.setBrush(QBrush(componentFillColor)) self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) @@ -784,7 +1059,7 @@ class PixmapItem(QGraphicsPixmapItem): return value class GlyphScene(QGraphicsScene): - def __init__(self, parent): + def __init__(self, parent, sceneAddedItems=None): super(GlyphScene, self).__init__(parent) self._editing = False self._integerPlane = True @@ -796,6 +1071,10 @@ class GlyphScene(QGraphicsScene): self._dataForUndo = [] self._dataForRedo = [] + # if this is an array, the Glyphview will clean up all contents + # when redrawing the active layer + self._sceneAddedItems = sceneAddedItems + font = self.font() font.setFamily("Roboto Mono") font.setFixedPitch(True) @@ -809,6 +1088,12 @@ class GlyphScene(QGraphicsScene): _glyphObject = property(_get_glyphObject, doc="Get the current glyph in the view.") + def _addRegisterItem(self, item): + """The parent object will take care of removing these again""" + if self._sceneAddedItems is not None: + self._sceneAddedItems.append(item) + self.addItem(item) + def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() @@ -832,7 +1117,7 @@ class GlyphScene(QGraphicsScene): return pos = event.scenePos() newPix = PixmapItem(pos.x(), pos.y(), dragPix) - self.addItem(newPix) + self._addRegisterItem(newPix) event.acceptProposedAction() def getItemForPoint(self, point): @@ -1023,7 +1308,7 @@ class GlyphScene(QGraphicsScene): else: lastContour.addPoint((x,y), "line") item = OnCurvePointItem(x, y, False, lastContour, lastContour[-1], self.getViewScale()) - self.addItem(item) + self._addRegisterItem(item) for _ in range(2): lineObj = HandleLineItem(0, 0, 0, 0, item) CPObject = OffCurvePointItem(0, 0, item) @@ -1038,7 +1323,7 @@ class GlyphScene(QGraphicsScene): nextC.addPoint((x,y), "move") item = OnCurvePointItem(x, y, False, self._glyphObject[-1], self._glyphObject[-1][-1], self.getViewScale()) - self.addItem(item) + self._addRegisterItem(item) for _ in range(2): lineObj = HandleLineItem(0, 0, 0, 0, item) CPObject = OffCurvePointItem(0, 0, item) @@ -1341,17 +1626,30 @@ class GlyphScene(QGraphicsScene): self._cachedIntersections = [] event.accept() + class GlyphView(QGraphicsView): - def __init__(self, glyph, parent=None): + def __init__(self, glyph, settings, parent=None): super(GlyphView, self).__init__(parent) - self._glyph = glyph - self._glyph.addObserver(self, "_glyphChanged", "Glyph.Changed") - self._glyph.layerSet.addObserver(self, "_layersChanged", "LayerSet.Changed") + + # wont change during lifetime + self._layerSet = layerSet = glyph.layerSet + self._name = glyph.name + self._settings = settings + + # we got the individual layers as keys => pathItem + # we got string keys => [list of scene items] like 'components' + self._sceneItems = {} + + # will change during lifetime + self._layer = glyph.layer + self._impliedPointSize = 1000 self._pointSize = None + # apparently unused code self._inverseScale = 0.1 self._scale = 10 + self._noPointSizePadding = 200 self._drawStroke = True self._showOffCurvePoints = True @@ -1359,15 +1657,12 @@ class GlyphView(QGraphicsView): self._showMetricsTitles = True self.setBackgroundBrush(QBrush(Qt.lightGray)) - self.setScene(GlyphScene(self)) + self.setScene(GlyphScene(self, self._getSceneItems('scene-added-items'))) font = self.font() font.setFamily("Roboto Mono") font.setFixedPitch(True) self.setFont(font) - self._currentLayerBox = QComboBox(self) - self._currentLayerBox.currentIndexChanged.connect(self._currentLayerChanged) - self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) #self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) @@ -1380,53 +1675,117 @@ class GlyphView(QGraphicsView): self.addBackground() self.addBlues() self.addHorizontalMetrics() - self.addOtherLayersOutlines() - self.addOutlines() - self.addComponents() - self.addAnchors() - self.addPoints() - def currentLayerBox(self): - return self._currentLayerBox + for layer in layerSet: + if self._name not in layer: + self._listenToLayer(layer) + else: + self._listenToGlyph(layer) + self._listenToLayerSet() + + self.changeCurrentLayer(self._layer) + + + @property + def _glyph(self): + # instead of a getter we could change the glyph each time + # self._layer is changed. However, it might be that the glyph + # is not ready when the layer is set + return self._layer[self._name] + + @property + def name(self): + return self._name + + @property + def layer(self): + return self._layer + + @property + def defaultWidth(self): + defaultLayer = self._layerSet[None] + return defaultLayer[self._name].width \ + if self._name in defaultLayer else 0 + + def _listenToLayerSet(self): + self._layerSet.addObserver(self, '_layerDeleted', 'LayerSet.LayerWillBeDeleted') + self._layerSet.addObserver(self, '_layerGenericVisualChange', 'LayerSet.LayerOrderChanged') + + def _listenToLayer(self, layer, remove=False): + if remove: + layer.removeObserver(self, 'Layer.GlyphAdded') + else: + layer.addObserver(self, '_layerGlyphAdded', 'Layer.GlyphAdded') + + def _layerGenericVisualChange(self, notification): + self.redraw() + + def _listenToGlyph(self, layer): + layer[self._name].addObserver(self, "_glyphChanged", "Glyph.Changed") + layer.addObserver(self, '_layerGenericVisualChange', 'Layer.ColorChanged') + + def layerAdded(self, layer): + self._listenToLayer(layer) + + def _layerGlyphAdded(self, notification): + glyphName = notification.data['name'] + # the layer will emit this for each new glyph + if glyphName != self._name: + return + layer = notification.object + self._listenToLayer(layer, remove=True) + self._listenToGlyph(layer) + self.redraw() + + def _layerDeleted(self, notification): + layerName = notification.data['name'] + layer = self._layerSet[layerName] + self._removeLayerPath(layer) 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() + glyph = notification.object + layer = glyph.layer + if layer is self._layer: + self.activeGlyphChanged() + else: + self.updateLayerPath(layer) + + def redraw(self): + self._getSceneItems('scene-added-items', clear=True) + self.updateLayerAssets() + self.drawAllLayers() + self.addAnchors() + self.addPoints() - # TODO: diagnose notifications count - def _layersChanged(self, notification): - self.redrawGlyph() - self.redrawOtherLayers() + def updateLayerAssets(self): + for item in self._getSceneItems('hMetricLabels'): + item.setPos(self._glyph.width, item.y()) - def redrawGlyph(self): - path = self._glyph.getRepresentation("defconQt.NoComponentsQPainterPath") + def activeGlyphChanged(self): + # For now, we'll assume not scene._blocked == moving UI points + # this will not be the case anymore when drag sidebearings pops up scene = self.scene() - scene._outlineItem.setPath(path) - if not scene._blocked: - # TODO: also rewind anchors and components - for item in scene.items(): - if isinstance(item, (OnCurvePointItem, ComponentItem, AnchorItem)): - scene.removeItem(item) - elif isinstance(item, VGuidelinesTextItem): - item.setPos(self._glyph.width, item.y()) + if scene._blocked: + self.updateActiveLayerPath() + return + self.updateActiveLayer() - self.addComponents() - self.addAnchors() - self.addPoints() - # For now, we'll assume not scene._blocked == moving UI points - # this will not be the case anymore when drag sidebearings pops up - scene._widthItem.setRect(0, -1000, self._glyph.width, 3000) + def updateActiveLayer(self): + self._getSceneItems('scene-added-items', clear=True) + self.updateLayerAssets() + + self.updateActiveLayerPath() + self.addComponents() + self.addAnchors() + self.addPoints() - def redrawOtherLayers(self): + # this is related to addBackground scene = self.scene() - for item in scene.items(): - # XXX: discriminate better - if isinstance(item, QGraphicsPathItem) and item.zValue() == -997: - scene.removeItem(item) - self.addOtherLayersOutlines() + scene._widthItem.setRect(0, -1000, self._glyph.width, 3000) def addBackground(self): scene = self.scene() @@ -1486,6 +1845,9 @@ class GlyphView(QGraphicsView): y = roundPosition(position) item = scene.addLine(-1000, y, 2000, y, QPen(metricsColor)) item.setZValue(-997) + + + labels = self._getSceneItems('hMetricLabels', clear=True) # text if self._showMetricsTitles:# and self._impliedPointSize > 150: fontSize = 9# * self._inverseScale @@ -1500,56 +1862,164 @@ class GlyphView(QGraphicsView): item.setFlag(QGraphicsItem.ItemIgnoresTransformations) item.setPos(width, y) item.setZValue(-997) + labels.append(item) scene.addItem(item) - def addOtherLayersOutlines(self): - comboBox = self._currentLayerBox - comboBox.blockSignals(True) - comboBox.clear() - scene = self.scene() - layerSet = self._glyph.layerSet - for layer in layerSet: - comboBox.addItem(layer.name, layer) - if layer == self._glyph.layer: - comboBox.setCurrentText(layer.name) + def getLayerColor(self, layer=None): + if layer is None: + layer = self._layer + if layer.color is not None: + return QColor.fromRgbF(*layer.color) + return None + + + def _getDrawingStyleForLayer(self, layer, kind=None): + isActive = layer is self._layer + isFilled = self._settings.activeLayerFilled \ + if isActive else self._settings.otherLayersFilled + isOutlined = not isFilled + useLayerColor = self._settings.activeLayerUseLayerColor \ + if isActive else self._settings.otherLayerUseLayerColor + + if useLayerColor: + brushcolor = self.getLayerColor(layer) or Qt.black + pencolor = brushcolor + else: + # default app colors + brushcolor = fillColor if kind != 'component' else componentFillColor + pencolor = Qt.black + + if isOutlined: + pen = QPen(pencolor) + brush = QBrush(Qt.NoBrush) + else: + pen = QPen(Qt.NoPen) + brush = QBrush(brushcolor) + + if isActive and not useLayerColor: + # The originally released app did fall back to QT's default + # behavior i.e. no pen defined, so I preserve this here + if isOutlined: brush = None # like Qt.NoBrush + else: pen = None # like Qt.black + return (pen, brush) + + + + @property + def _activeLayerZValue(self): + return -995 if not self._settings.activeLayerOnTop else -993 + + def drawAllLayers(self): + activeLayer = self._layer + # all layers before the active layer are -996 + # the active layer is -995 or -993 if self._settings.activeLayerOnTop == True + # all layers after the active layer are -996 + zValue = -996 + for layer in reversed(list(self._layerSet)): + if self._name not in layer: continue - if not self._glyph.name in layer: + isActiveLayer = layer is activeLayer + if isActiveLayer: + zValue = self._activeLayerZValue + elif not self._settings.drawOtherLayers: + self._removeLayerPath(layer) continue - path = layer[self._glyph.name].getRepresentation("defconQt.NoComponentsQPainterPath") - if layer.color is not None: - layerColor = QColor.fromRgbF(tuple(layer.color)) - else: - layerColor = Qt.black - item = scene.addPath(path, QPen(layerColor)) - item.setZValue(-997) - comboBox.addItem("New layer...", None) - comboBox.blockSignals(False) + self.drawLayer(layer, zValue) + if isActiveLayer: + zValue = -994 + + def _removeLayerPath(self, layer): + scene = self.scene() + item = self._sceneItems.get(layer, None) + if item is not None: + scene.removeItem(item) + if layer in self._sceneItems: + del self._sceneItems[layer] + + def drawLayer(self, layer, zValue): + scene = self.scene() + glyph = layer[self._name] + self._removeLayerPath(layer) + + isActiveLayer = layer is self._layer + if isActiveLayer: + representationKey = "defconQt.NoComponentsQPainterPath" + else: + representationKey = "defconQt.QPainterPath" + + path = glyph.getRepresentation(representationKey) + item = QGraphicsPathItem() + item.setPath(path) + + pen, brush = self._getDrawingStyleForLayer(layer) + if pen: item.setPen(pen) + if brush: item.setBrush(brush) + + item.setZValue(zValue) + + self._sceneItems[layer] = item + scene.addItem(item) + if isActiveLayer: + # FIXME: don't like this + scene._outlineItem = item + self.addComponents() + return item - def addOutlines(self): + def updateActiveLayerPath(self): + self.updateLayerPath(self._layer, representationKey="defconQt.NoComponentsQPainterPath") + + def updateLayerPath(self, layer, representationKey="defconQt.QPainterPath"): + glyph = layer[self._name] scene = self.scene() - path = self._glyph.getRepresentation("defconQt.NoComponentsQPainterPath") - scene._outlineItem = scene.addPath(path, brush=QBrush(fillColor)) - scene._outlineItem.setZValue(-995) + path = glyph.getRepresentation(representationKey) + self._sceneItems[layer].setPath(path) + + def _getSceneItems(self, key, clear=False): + items = self._sceneItems.get(key, None) + if items is None: + items = [] + self._sceneItems[key] = items + elif clear: + scene = self.scene() + for item in items: + scene.removeItem(item) + del items[:] + return items def addComponents(self): scene = self.scene() - font = self._glyph.getParent() - for component in self._glyph.components: - glyph = font[component.baseGlyph] - path = glyph.getRepresentation("defconQt.QPainterPath") + layer = self._layer + glyph = self._glyph + + pen, brush = self._getDrawingStyleForLayer(layer, kind='component') + components = self._getSceneItems('components', clear=True) + for component in glyph.components: + if component.baseGlyph not in layer: + continue + componentGlyph = layer[component.baseGlyph] + path = componentGlyph.getRepresentation("defconQt.QPainterPath") item = ComponentItem(path, component) - item.setZValue(-996) + if pen: item.setPen(pen) + if brush: item.setBrush(brush) + item.setZValue(self._activeLayerZValue) + components.append(item) scene.addItem(item) def addAnchors(self): scene = self.scene() + anchors = self._getSceneItems('anchors', clear=True) + for anchor in self._glyph.anchors: item = AnchorItem(anchor, self.transform().m11()) - item.setZValue(-996) + item.setZValue(-992) + anchors.append(item) scene.addItem(item) def addPoints(self): scene = self.scene() + + pointItems = self._getSceneItems('points', clear=True) + # work out appropriate sizes and # skip if the glyph is too small pointSize = self._impliedPointSize @@ -1582,6 +2052,7 @@ class GlyphView(QGraphicsView): points.append((x, y)) item = OnCurvePointItem(x, y, onCurve.isSmooth, self._glyph[onCurve.contourIndex], self._glyph[onCurve.contourIndex][onCurve.pointIndex], scale) + pointItems.append(item) scene.addItem(item) # off curve for CP in [onCurve.prevCP, onCurve.nextCP]: @@ -1637,7 +2108,7 @@ class GlyphView(QGraphicsView): self._drawTextAtPoint(text, attributes, (posX, posY), 3) ''' - def createAnchor(self): + def createAnchor(self, *args): scene = self.scene() pos = scene._rightClickPos if scene._integerPlane: @@ -1651,51 +2122,42 @@ class GlyphView(QGraphicsView): anchor.name = newAnchorName self._glyph.appendAnchor(anchor) - def createComponent(self): + def createComponent(self, *args): newGlyph, ok = AddComponentDialog.getNewGlyph(self, self._glyph) if ok and newGlyph is not None: component = Component() component.baseGlyph = newGlyph.name self._glyph.appendComponent(component) - def _currentLayerChanged(self, newLayerIndex): - comboBox = self.sender() - newLayer = comboBox.itemData(newLayerIndex) - if newLayer is None: - # add a new layer - newLayerName, ok = AddLayerDialog.getNewLayerName(self) - if ok: - self._glyph.layerSet.newLayer(newLayerName) - self._currentLayerBox.blockSignals(True) - self._currentLayerBox.setCurrentText(newLayerName) - self._currentLayerBox.blockSignals(False) - newLayer = self._glyph.layerSet[newLayerName] - else: - return - if not self._glyph.name in newLayer: - newLayer.newGlyph(self._glyph.name) - # TODO: generalize this out, can’t use newStandardGlyph unfortunately - newLayer[self._glyph.name].width = self._glyph.width - newLayer[self._glyph.name].template = True - newGlyph = newLayer[self._glyph.name] - self.setGlyph(newGlyph) - - def setGlyph(self, glyph): + def _makeLayerGlyph(self, layer): + name = self._name + layer.newGlyph(name) + glyph = layer[name] + # TODO: generalize this out, can’t use newStandardGlyph unfortunately + glyph.width = self.defaultWidth + + # This prevents the empty glyph from being saved. + # TGlyph sets it to False when the glyph is marked as dirty + glyph.template = True + return glyph + + def changeCurrentLayer(self, layer): + name = self._name + + # set current layer + self._layer = layer + + if name in layer: + # please redraw + self.redraw() + else: + # no need to redraw, will happen within _layerGlyphAdded + self._makeLayerGlyph(layer) + + # TODO: Undo data does not keep track of different layers scene = self.scene() - self._glyph.removeObserver(self, "Glyph.Changed") - self._glyph.layerSet.removeObserver(self, "LayerSet.Changed") - # TODO: consider creating a new scene instead of zeroing things out - # manually scene._dataForUndo = [] scene._dataForRedo = [] - self._glyph = glyph - app = QApplication.instance() - app.setCurrentGlyph(glyph) - self._glyph.addObserver(self, "_glyphChanged", "Glyph.Changed") - self._glyph.layerSet.addObserver(self, "_layersChanged", "LayerSet.Changed") - self.parent().setWindowTitle(self._glyph.name, self._glyph.getParent()) - self.redrawGlyph() - self.redrawOtherLayers() def showEvent(self, event): super(GlyphView, self).showEvent(event) |
