From 1a889dc612c036763d80815a00d4b9a8fc73ba78 Mon Sep 17 00:00:00 2001 From: Adrien Tétar Date: Thu, 29 Oct 2015 21:09:19 +0100 Subject: meta: shufflings, loadMostRecentFile, metricsWindow: canvas key shortcuts --- Lib/defconQt/fontView.py | 203 ++++++---- Lib/defconQt/glyphView.py | 4 +- Lib/defconQt/metricsWindow.py | 879 ++++++++++++++++++++++++++++++++++++++++++ Lib/defconQt/spaceCenter.py | 826 --------------------------------------- 4 files changed, 1003 insertions(+), 909 deletions(-) create mode 100644 Lib/defconQt/metricsWindow.py delete mode 100644 Lib/defconQt/spaceCenter.py (limited to 'Lib') diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py index b0cc7e9..523719f 100644 --- a/Lib/defconQt/fontView.py +++ b/Lib/defconQt/fontView.py @@ -1,3 +1,4 @@ +from defconQt import __version__ from defconQt.featureTextEditor import MainEditWindow from defconQt.fontInfo import TabDialog from defconQt.glyphCollectionView import GlyphCollectionWidget @@ -8,7 +9,7 @@ from defconQt.scriptingWindow import MainScriptingWindow from defconQt.objects.defcon import GlyphSet, TFont, TGlyph from defconQt.util import platformSpecific from defcon import Color, Component -from defconQt.spaceCenter import MainSpaceWindow, comboBoxItems +from defconQt.metricsWindow import MainMetricsWindow, comboBoxItems from PyQt5.QtCore import ( pyqtSignal, QEvent, QMimeData, QRegularExpression, QSettings, Qt) from PyQt5.QtGui import ( @@ -16,13 +17,14 @@ from PyQt5.QtGui import ( QRegularExpressionValidator, QTextCursor) from PyQt5.QtWidgets import ( QAbstractItemView, QAction, QApplication, QCheckBox, QComboBox, QDialog, - QDialogButtonBox, QErrorMessage, QFileDialog, QGridLayout, QGroupBox, - QLabel, QLineEdit, QListWidget, QListWidgetItem, QMainWindow, QMenu, - QMessageBox, QPlainTextEdit, QPushButton, QRadioButton, QSlider, QSplitter, - QTabWidget, QTextEdit, QToolTip, QVBoxLayout, QWidget) + QDialogButtonBox, QFileDialog, QGridLayout, QGroupBox, QLabel, QLineEdit, + QListWidget, QListWidgetItem, QMainWindow, QMenu, QMessageBox, + QPlainTextEdit, QPushButton, QRadioButton, QSlider, QSplitter, QTabWidget, + QTextEdit, QToolTip, QVBoxLayout, QWidget) from collections import OrderedDict import os import pickle +import platform import traceback cannedDesign = [ @@ -114,11 +116,11 @@ class Application(QApplication): MAX_RECENT_FILES = 6 -class InspectorWindow(QWidget): +class InfoPanel(QWidget): def __init__(self): - super(InspectorWindow, self).__init__(flags=Qt.Tool) - self.setWindowTitle("Inspector") + super().__init__(flags=Qt.Tool) + self.setWindowTitle("Info Panel") self._blocked = False self._glyph = None @@ -248,7 +250,8 @@ class InspectorWindow(QWidget): self.setLayout(mainLayout) def showEvent(self, event): - super(InspectorWindow, self).showEvent(event) + super().showEvent(event) + # TODO: does not put window on secondary screen if applicable screenRect = QApplication.desktop().screenGeometry() widgetRect = self.frameGeometry() x = screenRect.width() - (widgetRect.width() + 20) @@ -381,11 +384,11 @@ class InspectorWindow(QWidget): self._blocked = False -class AddGlyphDialog(QDialog): +class AddGlyphsDialog(QDialog): # TODO: implement Frederik's Glyph Construction Builder def __init__(self, currentGlyphs=None, parent=None): - super(AddGlyphDialog, self).__init__(parent) + super(AddGlyphsDialog, self).__init__(parent) self.setWindowModality(Qt.WindowModal) self.setWindowTitle("Add glyphs…") self.currentGlyphs = currentGlyphs @@ -398,8 +401,8 @@ class AddGlyphDialog(QDialog): for name, glyphNames in glyphSets.items(): self.importCharDrop.addItem(name, glyphNames) self.importCharDrop.currentIndexChanged[int].connect(self.importGlyphs) - self.addGlyphEdit = QPlainTextEdit(self) - self.addGlyphEdit.setFocus(True) + self.addGlyphsEdit = QPlainTextEdit(self) + self.addGlyphsEdit.setFocus(True) self.addUnicodeBox = QCheckBox("Add Unicode", self) self.addUnicodeBox.setChecked(True) @@ -415,7 +418,7 @@ class AddGlyphDialog(QDialog): l = 0 layout.addWidget(self.importCharDrop, l, 3, 1, 2) l += 1 - layout.addWidget(self.addGlyphEdit, l, 0, 1, 5) + layout.addWidget(self.addGlyphsEdit, l, 0, 1, 5) l += 1 layout.addWidget(self.addUnicodeBox, l, 0) layout.addWidget(self.addAsTemplateBox, l, 1) @@ -435,7 +438,7 @@ class AddGlyphDialog(QDialog): sortFont=dialog.sortFontBox.isChecked(), ) newGlyphNames = [] - for name in dialog.addGlyphEdit.toPlainText().split(): + for name in dialog.addGlyphsEdit.toPlainText().split(): if name not in dialog.currentGlyphNames: newGlyphNames.append(name) return (newGlyphNames, params, result) @@ -444,7 +447,7 @@ class AddGlyphDialog(QDialog): if index == 0: return glyphNames = self.importCharDrop.currentData() - editorNames = self.addGlyphEdit.toPlainText().split() + editorNames = self.addGlyphsEdit.toPlainText().split() currentNames = set(self.currentGlyphNames) ^ set(editorNames) changed = False for name in glyphNames: @@ -452,12 +455,12 @@ class AddGlyphDialog(QDialog): changed = True editorNames.append(name) if changed: - self.addGlyphEdit.setPlainText(" ".join(editorNames)) - cursor = self.addGlyphEdit.textCursor() + self.addGlyphsEdit.setPlainText(" ".join(editorNames)) + cursor = self.addGlyphsEdit.textCursor() cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor) - self.addGlyphEdit.setTextCursor(cursor) + self.addGlyphsEdit.setTextCursor(cursor) self.importCharDrop.setCurrentIndex(0) - self.addGlyphEdit.setFocus(True) + self.addGlyphsEdit.setFocus(True) class SortDialog(QDialog): @@ -467,7 +470,7 @@ class SortDialog(QDialog): self.setWindowModality(Qt.WindowModal) self.setWindowTitle("Sort…") - self.smartSortBox = QRadioButton("Smart sort", self) + self.smartSortBox = QRadioButton("Optimized sort", self) self.smartSortBox.setToolTip("A combination of simple, complex and " "custom sorts that give optimized " "ordering results.") @@ -624,10 +627,16 @@ class MainWindow(QMainWindow): self.collectionWidget = GlyphCollectionWidget(self) self._font = None self._sortDescriptor = None - if font is None: - self.newFile(True) - else: + settings = QSettings() + loadRecentFile = settings.value("core/loadRecentFile", False, bool) + if font is None and loadRecentFile: + recentFiles = settings.value("core/recentFiles", [], type=str) + if len(recentFiles): + self.openFile(recentFiles[0], True) + elif font is not None: self.font = font + if self._font is None: + self.newFile(True) self.collectionWidget.glyphSelectedCallback = self._selectionChanged self.collectionWidget.doubleClickCallback = self._glyphOpened # TODO: should default be True or False? @@ -640,7 +649,7 @@ class MainWindow(QMainWindow): fileMenu.addAction("&New…", self.newFile, QKeySequence.New) fileMenu.addAction("&Open…", self.openFile, QKeySequence.Open) # recent files - self.recentFilesMenu = QMenu("Open &recent…", self) + self.recentFilesMenu = QMenu("Open &Recent…", self) for i in range(MAX_RECENT_FILES): action = QAction(self.recentFilesMenu) action.setVisible(False) @@ -650,14 +659,14 @@ class MainWindow(QMainWindow): fileMenu.addMenu(self.recentFilesMenu) fileMenu.addSeparator() fileMenu.addAction("&Save", self.saveFile, QKeySequence.Save) - fileMenu.addAction("Save &as…", self.saveFileAs, QKeySequence.SaveAs) + fileMenu.addAction("Save &As…", self.saveFileAs, QKeySequence.SaveAs) fileMenu.addAction("Export…", self.export) - fileMenu.addAction("Reload from disk", self.reload) + fileMenu.addAction("Reload From Disk", self.reload) fileMenu.addAction("E&xit", self.close, QKeySequence.Quit) menuBar.addMenu(fileMenu) editMenu = QMenu("&Edit", self) - markColorMenu = QMenu("Flag color", self) + markColorMenu = QMenu("Flag Color", self) pixmap = QPixmap(24, 24) none = markColorMenu.addAction("None", self.markColor) none.setData(None) @@ -675,7 +684,7 @@ class MainWindow(QMainWindow): green.setData(QColor(Qt.green)) editMenu.addMenu(markColorMenu) editMenu.addAction("Copy", self.copy, QKeySequence.Copy) - editMenu.addAction("Copy as component", + editMenu.addAction("Copy As Component", self.copyAsComponent, "Ctrl+Alt+C") editMenu.addAction("Paste", self.paste, QKeySequence.Paste) editMenu.addSeparator() @@ -685,23 +694,23 @@ class MainWindow(QMainWindow): fontMenu = QMenu("&Font", self) # 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+G") - fontMenu.addAction("Font &info", self.fontInfo, "Ctrl+Alt+I") - fontMenu.addAction("Font &features", self.fontFeatures, "Ctrl+Alt+F") + fontMenu.addAction("&Add Glyphs…", self.addGlyphs, "Ctrl+G") + fontMenu.addAction("Font &Info", self.fontInfo, "Ctrl+Alt+I") + fontMenu.addAction("Font &Features", self.fontFeatures, "Ctrl+Alt+F") fontMenu.addSeparator() - fontMenu.addAction("Sort…", self.sortGlyphs) + fontMenu.addAction("&Sort…", self.sortGlyphs) menuBar.addMenu(fontMenu) pythonMenu = QMenu("&Python", self) - pythonMenu.addAction("Scripting &window", self.scripting, "Ctrl+Alt+R") + pythonMenu.addAction("Scripting &Window", self.scripting, "Ctrl+Alt+R") menuBar.addMenu(pythonMenu) windowMenu = QMenu("&Windows", self) - action = windowMenu.addAction("&Inspector", self.inspector, "Ctrl+I") + action = windowMenu.addAction("&Info Panel", self.infoPanel, "Ctrl+I") # XXX: we're getting duplicate shortcut when we spawn a new window... action.setShortcutContext(Qt.ApplicationShortcut) - windowMenu.addAction("&Space center", self.spaceCenter, "Ctrl+Alt+S") - windowMenu.addAction("&Groups window", self.fontGroups, "Ctrl+Alt+G") + windowMenu.addAction("&Metrics Window", self.metrics, "Ctrl+Alt+S") + windowMenu.addAction("&Groups Window", self.groups, "Ctrl+Alt+G") menuBar.addMenu(windowMenu) helpMenu = QMenu("&Help", self) @@ -753,7 +762,7 @@ class MainWindow(QMainWindow): else: self.font = font - def openFile(self, path=None): + def openFile(self, path=None, stickToSelf=False): if not path: path, ok = QFileDialog.getOpenFileName( self, "Open File", '', @@ -765,7 +774,7 @@ class MainWindow(QMainWindow): if ".plist" in path: path = os.path.dirname(path) for window in QApplication.topLevelWidgets(): - if (isinstance(window, MainWindow) + if (isinstance(window, MainWindow) and window._font is not None and window._font.path == path): window.raise_() return @@ -774,8 +783,11 @@ class MainWindow(QMainWindow): except: print(traceback.format_exc()) return - window = MainWindow(font) - window.show() + if not stickToSelf: + window = MainWindow(font) + window.show() + else: + self.font = font def openRecentFont(self): fontPath = self.sender().toolTip() @@ -822,12 +834,12 @@ class MainWindow(QMainWindow): try: from ufo2fdk import haveFDK, OTFCompiler except Exception as e: - errorMessage = QErrorMessage(self) - errorMessage.showMessage(e) + title = e.__class__.__name__ + QMessageBox.critical(self, title, str(e)) return if not haveFDK(): - errorMessage = QErrorMessage(self) - errorMessage.showMessage("The Adobe FDK could not be found.") + QMessageBox.critical(self, "Missing dependency", "The Adobe FDK " + "could not be found.") return path, ok = QFileDialog.getSaveFileName(self, "Save File", None, @@ -1095,32 +1107,31 @@ class MainWindow(QMainWindow): else: self.fontFeaturesWindow.raise_() - def spaceCenter(self): + def metrics(self): # TODO: see up here - # TODO: show selection in a space center, rewind selection if we raise - # window (rf) - if not (hasattr(self, 'spaceCenterWindow') - and self.spaceCenterWindow.isVisible()): - self.spaceCenterWindow = MainSpaceWindow(self.font, parent=self) - self.spaceCenterWindow.show() + if not (hasattr(self, 'metricsWindow') + and self.metricsWindow.isVisible()): + self.metricsWindow = MainMetricsWindow(self.font, parent=self) + self.metricsWindow.show() else: - self.spaceCenterWindow.raise_() + self.metricsWindow.raise_() + # TODO: decouple selection = self.collectionWidget.selection if selection: glyphs = [] for item in sorted(selection): glyph = self.collectionWidget.glyphs[item] glyphs.append(glyph) - self.spaceCenterWindow.setGlyphs(glyphs) + self.metricsWindow.setGlyphs(glyphs) - def fontGroups(self): + def groups(self): # TODO: see up here - if not (hasattr(self, 'fontGroupsWindow') - and self.fontGroupsWindow.isVisible()): - self.fontGroupsWindow = GroupsWindow(self.font, self) - self.fontGroupsWindow.show() + if not (hasattr(self, 'groupsWindow') + and self.groupsWindow.isVisible()): + self.groupsWindow = GroupsWindow(self.font, self) + self.groupsWindow.show() else: - self.fontGroupsWindow.raise_() + self.groupsWindow.raise_() def scripting(self): app = QApplication.instance() @@ -1132,18 +1143,18 @@ class MainWindow(QMainWindow): else: app.scriptingWindow.show() - def inspector(self): + def infoPanel(self): app = QApplication.instance() - if not hasattr(app, 'inspectorWindow'): - app.inspectorWindow = InspectorWindow() - app.inspectorWindow.show() - elif app.inspectorWindow.isVisible(): + if not hasattr(app, 'infoPanelWindow'): + app.infoPanelWindow = InfoPanel() + app.infoPanelWindow.show() + elif app.infoPanelWindow.isVisible(): # TODO: do this only if the widget is user-visible, otherwise the # key press feels as if it did nothing # toggle - app.inspectorWindow.close() + app.infoPanelWindow.close() else: - app.inspectorWindow.show() + app.infoPanelWindow.show() def sortGlyphs(self): sortDescriptor, ok = SortDialog.getDescriptor(self, @@ -1151,9 +1162,9 @@ class MainWindow(QMainWindow): if ok: self.sortDescriptor = sortDescriptor - def addGlyph(self): + def addGlyphs(self): glyphs = self.collectionWidget.glyphs - newGlyphNames, params, ok = AddGlyphDialog.getNewGlyphNames( + newGlyphNames, params, ok = AddGlyphsDialog.getNewGlyphNames( self, glyphs) if ok: sortFont = params.pop("sortFont") @@ -1171,12 +1182,19 @@ class MainWindow(QMainWindow): def about(self): name = QApplication.applicationName() - QMessageBox.about( - self, "About {}".format(name), - "

About {}

" - "

I am a new UFO-centric font editor and I aim to bring " - "the robofab ecosystem to all main operating systems, " - "in a fast and dependency-free package.

".format(name)) + domain = QApplication.organizationDomain() + text = "

About {}

" \ + "

I am a new UFO-centric font editor and I aim to bring " \ + "the robofab ecosystem to all main operating systems, " \ + "in a fast and dependency-free package.

" \ + "

Version {} – Python {}.".format( + name, __version__, platform.python_version()) + if domain: + text += "
See {d} for more " \ + "information.

".format(d=domain) + else: + text += "

" + QMessageBox.about(self, "About {}".format(name), text) class SettingsDialog(QDialog): @@ -1188,7 +1206,8 @@ class SettingsDialog(QDialog): self.tabWidget = QTabWidget(self) self.tabWidget.addTab(GlyphSetTab(self), "Glyph sets") - self.tabWidget.addTab(SpaceCenterTab(self), "Space center") + self.tabWidget.addTab(MetricsWindowTab(self), "Metrics Window") + self.tabWidget.addTab(MiscTab(self), "Misc") buttonBox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) @@ -1232,7 +1251,7 @@ def readGlyphSets(settings=None): class GlyphSetTab(QWidget): def __init__(self, parent=None): - super(GlyphSetTab, self).__init__(parent) + super().__init__(parent) settings = QSettings() self.defaultGlyphSetBox = QCheckBox("Default glyph set:", self) @@ -1366,16 +1385,16 @@ class GlyphSetTab(QWidget): settings.setValue("settings/defaultGlyphSet", defaultGlyphSet) -class SpaceCenterTab(QTabWidget): +class MetricsWindowTab(QTabWidget): def __init__(self, parent=None): - super(SpaceCenterTab, self).__init__(parent) + super().__init__(parent) settings = QSettings() self.inputTextLabel = QLabel("Default text:", self) self.inputTextList = QListWidget(self) self.inputTextList.setDragDropMode(QAbstractItemView.InternalMove) - entries = settings.value("spaceCenter/comboBoxItems", comboBoxItems, + entries = settings.value("metricsWindow/comboBoxItems", comboBoxItems, str) for entry in entries: item = QListWidgetItem(entry, self.inputTextList) @@ -1414,4 +1433,26 @@ class SpaceCenterTab(QTabWidget): item = self.inputTextList.item(i) entries.append(item.text()) settings = QSettings() - settings.setValue("spaceCenter/comboBoxItems", entries) + settings.setValue("metricsWindow/comboBoxItems", entries) + + +class MiscTab(QTabWidget): + + def __init__(self, parent=None): + super().__init__(parent) + + settings = QSettings() + loadRecentFile = settings.value("core/loadRecentFile", False, bool) + self.loadRecentFileBox = QCheckBox("Load most recent file on start", + self) + self.loadRecentFileBox.setChecked(loadRecentFile) + + layout = QVBoxLayout(self) + l = 0 + layout.addWidget(self.loadRecentFileBox, l) + self.setLayout(layout) + + def writeValues(self): + settings = QSettings() + loadRecentFile = self.loadRecentFileBox.isChecked() + settings.setValue("core/loadRecentFile", loadRecentFile) diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py index 87232c3..c9b483a 100644 --- a/Lib/defconQt/glyphView.py +++ b/Lib/defconQt/glyphView.py @@ -181,7 +181,7 @@ class AddLayerDialog(QDialog): layout.addWidget(layerNameLabel, l, 0) layout.addWidget(self.layerNameEdit, l, 1) l += 1 - layout.addWidget(buttonBox, l, 2) + layout.addWidget(buttonBox, l, 0, 1, 2) self.setLayout(layout) @classmethod @@ -302,7 +302,7 @@ class MainGfxWindow(QMainWindow): menuBar.addMenu(fileMenu) glyphMenu = QMenu("&Glyph", self) - glyphMenu.addAction("&Jump", self.changeGlyph, "J") + glyphMenu.addAction("&Go to…", self.changeGlyph, "G") menuBar.addMenu(glyphMenu) self._displaySettings = DisplayStyleSettings( diff --git a/Lib/defconQt/metricsWindow.py b/Lib/defconQt/metricsWindow.py new file mode 100644 index 0000000..27689ab --- /dev/null +++ b/Lib/defconQt/metricsWindow.py @@ -0,0 +1,879 @@ +from defconQt import icons_db # noqa +from defconQt.glyphCollectionView import arrowKeys, cellSelectionColor +from defconQt.glyphView import MainGfxWindow +from defconQt.objects.defcon import TGlyph +from getpass import getuser +from PyQt5.QtCore import QEvent, QSettings, QSize, Qt +from PyQt5.QtGui import ( + QBrush, QColor, QIcon, QIntValidator, QKeySequence, QPainter, QPalette, + QPen) +from PyQt5.QtWidgets import ( + QAbstractItemView, QActionGroup, QApplication, QComboBox, QLineEdit, QMenu, + QPushButton, QScrollArea, QStyledItemDelegate, QTableWidget, + QTableWidgetItem, QVBoxLayout, QSizePolicy, QToolBar, QWidget) +import re + +comboBoxItems = [ + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "0123456789", + "nn/? nono/? oo", + "HH/? HOHO/? OO", +] + +defaultPointSize = 150 +glyphSelectionColor = QColor(cellSelectionColor) +glyphSelectionColor.setAlphaF(.09) + +escapeRep = { + "//": "/slash ", + "\\n": "\u2029", +} +escapeRep = dict((re.escape(k), v) for k, v in escapeRep.items()) +escapeRe = re.compile("|".join(escapeRep.keys())) + + +class MainMetricsWindow(QWidget): + + def __init__(self, font, string=None, pointSize=defaultPointSize, + parent=None): + super().__init__(parent, Qt.Window) + + if string is None: + try: + string = getuser() + except: + string = "World" + string = "Hello %s" % string + # TODO: drop self.font and self.glyphs, store in the widgets only + self.font = font + self.glyphs = [] + self.toolbar = FontToolBar(pointSize, self) + self.canvas = GlyphsCanvas(font, pointSize, self) + self.table = SpaceTable(self) + self.toolbar.comboBox.currentIndexChanged[ + str].connect(self.canvas.setPointSize) + self.canvas.doubleClickCallback = self._glyphOpened + self.canvas.pointSizeChangedCallback = self.toolbar.setPointSize + self.canvas.selectionChangedCallback = self.table.setCurrentGlyph + self.table.selectionChangedCallback = self.canvas.setSelected + + self.toolbar.textField.editTextChanged.connect(self._textChanged) + self.toolbar.textField.setEditText(string) + app = QApplication.instance() + app.currentGlyphChanged.connect(self._textChanged) + + layout = QVBoxLayout(self) + layout.addWidget(self.toolbar) + layout.addWidget(self.canvas.scrollArea()) + layout.addWidget(self.table) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + self.setLayout(layout) + self.resize(600, 500) + + self.font.info.addObserver(self, "_fontInfoChanged", "Info.Changed") + + self.setWindowTitle("Metrics Window – %s %s" % ( + self.font.info.familyName, self.font.info.styleName)) + + def setupFileMenu(self): + fileMenu = QMenu("&File", self) + fileMenu.addAction("&Save...", self.save, QKeySequence.Save) + fileMenu.addAction("E&xit", self.close, QKeySequence.Quit) + self.menuBar().addMenu(fileMenu) + + def close(self): + self.font.info.removeObserver(self, "Info.Changed") + self._unsubscribeFromGlyphs() + super().close() + + def _fontInfoChanged(self, notification): + self.canvas.fetchFontMetrics() + self.canvas.update() + + def _glyphChanged(self, notification): + if not self.canvas._editing: + self.canvas.update() + if not self.table._editing: + self.table.updateCells(self.canvas._editing) + + def _glyphOpened(self, glyph): + glyphViewWindow = MainGfxWindow(glyph, self.parent()) + glyphViewWindow.show() + + def _textChanged(self): + def fetchGlyphs(glyphNames, leftGlyphs=[], rightGlyphs=[]): + ret = [] + for name in glyphNames: + if name == "\u2029": + glyph = TGlyph() + glyph.unicode = 2029 + ret.append(glyph) + elif name in self.font: + ret.extend(leftGlyphs) + ret.append(self.font[name]) + ret.extend(rightGlyphs) + return ret + + # unsubscribe from the old glyphs + self._unsubscribeFromGlyphs() + # subscribe to the new glyphs + left = self.textToGlyphNames(self.toolbar.leftTextField.text()) + newText = self.textToGlyphNames(self.toolbar.textField.currentText()) + right = self.textToGlyphNames(self.toolbar.rightTextField.text()) + leftGlyphs = fetchGlyphs(left) + rightGlyphs = fetchGlyphs(right) + finalGlyphs = fetchGlyphs(newText, leftGlyphs, rightGlyphs) + self._subscribeToGlyphs(finalGlyphs) + # set the records into the view + self.canvas.setGlyphs(self.glyphs) + self.table.setGlyphs(self.glyphs) + + # Tal Leming. Edited. + def textToGlyphNames(self, text): + def catchCompile(): + if compileStack[0] == "?": + glyph = app.currentGlyph() + if glyph is not None: + glyphNames.append(glyph.name) + elif compileStack: + glyphNames.append("".join(compileStack)) + + app = QApplication.instance() + # escape //, \n + text = escapeRe.sub(lambda m: escapeRep[re.escape(m.group(0))], text) + # + glyphNames = [] + compileStack = None + for c in text: + # start a glyph name compile. + if c == "/": + # finishing a previous compile. + if compileStack is not None: + # only add the compile if something has been added to the + # stack. + if compileStack: + glyphNames.append("".join(compileStack)) + # reset the stack. + compileStack = [] + # adding to or ending a glyph name compile. + elif compileStack is not None: + # space. conclude the glyph name compile. + if c == " ": + # only add the compile if something has been added to the + # stack. + catchCompile() + compileStack = None + # add the character to the stack. + else: + compileStack.append(c) + # adding a character that needs to be converted to a glyph name. + else: + uni = ord(c) + if uni == 0x2029: + glyphName = c + else: + glyphName = self.font.unicodeData.glyphNameForUnicode(uni) + glyphNames.append(glyphName) + # catch remaining compile. + if compileStack is not None and compileStack: + catchCompile() + return glyphNames + + def _subscribeToGlyphs(self, glyphs): + self.glyphs = glyphs + + handledGlyphs = set() + for glyph in self.glyphs: + if glyph in handledGlyphs: + continue + handledGlyphs.add(glyph) + glyph.addObserver(self, "_glyphChanged", "Glyph.Changed") + + def _unsubscribeFromGlyphs(self): + handledGlyphs = set() + for glyph in self.glyphs: + if glyph in handledGlyphs: + continue + handledGlyphs.add(glyph) + glyph.removeObserver(self, "Glyph.Changed") + # self.glyphs = None + + def setGlyphs(self, glyphs): + # unsubscribe from the old glyphs + self._unsubscribeFromGlyphs() + # subscribe to the new glyphs + self._subscribeToGlyphs(glyphs) + glyphNames = [] + for glyph in glyphs: + if glyph.unicode: + glyphNames.append(chr(glyph.unicode)) + else: + glyphNames.append("".join(("/", glyph.name, " "))) + self.toolbar.textField.setEditText("".join(glyphNames)) + # set the records into the view + self.canvas.setGlyphs(self.glyphs) + self.table.setGlyphs(self.glyphs) + +pointSizes = [50, 75, 100, 125, 150, 200, 250, 300, 350, 400, 450, 500] + + +class FontToolBar(QToolBar): + + def __init__(self, pointSize, parent=None): + super(FontToolBar, self).__init__(parent) + auxiliaryWidth = self.fontMetrics().width('0') * 8 + self.leftTextField = QLineEdit(self) + self.leftTextField.setMaximumWidth(auxiliaryWidth) + self.textField = QComboBox(self) + self.textField.setEditable(True) + completer = self.textField.completer() + completer.setCaseSensitivity(Qt.CaseSensitive) + self.textField.setCompleter(completer) + # XXX: had to use Maximum because Preferred did entend the widget(?) + self.textField.setSizePolicy(QSizePolicy.Expanding, + QSizePolicy.Maximum) + items = QSettings().value("metricsWindow/comboBoxItems", comboBoxItems, + str) + self.textField.addItems(items) + self.rightTextField = QLineEdit(self) + self.rightTextField.setMaximumWidth(auxiliaryWidth) + self.leftTextField.textEdited.connect(self.textField.editTextChanged) + self.rightTextField.textEdited.connect(self.textField.editTextChanged) + self.comboBox = QComboBox(self) + self.comboBox.setEditable(True) + self.comboBox.setValidator(QIntValidator(self)) + for p in pointSizes: + self.comboBox.addItem(str(p)) + self.comboBox.setEditText(str(pointSize)) + + self.configBar = QPushButton(self) + self.configBar.setFlat(True) + self.configBar.setIcon(QIcon(":/resources/settings.svg")) + self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px") + self.toolsMenu = QMenu(self) + showKerning = self.toolsMenu.addAction( + "Show Kerning", self.showKerning) + showKerning.setCheckable(True) + showMetrics = self.toolsMenu.addAction( + "Show Metrics", self.showMetrics) + showMetrics.setCheckable(True) + self.toolsMenu.addSeparator() + wrapLines = self.toolsMenu.addAction("Wrap lines", self.wrapLines) + wrapLines.setCheckable(True) + noWrapLines = self.toolsMenu.addAction("No wrap", self.noWrapLines) + noWrapLines.setCheckable(True) + self.toolsMenu.addSeparator() + verticalFlip = self.toolsMenu.addAction( + "Vertical flip", self.verticalFlip) + verticalFlip.setCheckable(True) + """ + lineHeight = QWidgetAction(self.toolsMenu) + lineHeight.setText("Line height:") + lineHeightSlider = QSlider(Qt.Horizontal, self) + # QSlider works with integers so we'll just divide by 100 what comes + # out of it + lineHeightSlider.setMinimum(80) + lineHeightSlider.setMaximum(160) + lineHeightSlider.setValue(100) + #lineHeightSlider.setContentsMargins(30, 0, 30, 0) + lineHeightSlider.valueChanged.connect(self.lineHeight) + lineHeight.setDefaultWidget(lineHeightSlider) + self.toolsMenu.addAction(lineHeight) + """ + + wrapLinesGroup = QActionGroup(self) + wrapLinesGroup.addAction(wrapLines) + wrapLinesGroup.addAction(noWrapLines) + wrapLines.setChecked(True) + # self.toolsMenu.setActiveAction(wrapLines) + self.configBar.setMenu(self.toolsMenu) + + self.addWidget(self.leftTextField) + self.addWidget(self.textField) + self.addWidget(self.rightTextField) + self.addWidget(self.comboBox) + self.addWidget(self.configBar) + + def showEvent(self, event): + super(FontToolBar, self).showEvent(event) + self.textField.setFocus(True) + + 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 lineHeight(self, value): + self.parent().canvas.setLineHeight(value / 100) + + def wrapLines(self): + self.parent().canvas.setWrapLines(True) + + def noWrapLines(self): + self.parent().canvas.setWrapLines(False) + + +class GlyphsCanvas(QWidget): + + def __init__(self, font, pointSize=defaultPointSize, parent=None): + super(GlyphsCanvas, self).__init__(parent) + self.setAttribute(Qt.WA_KeyCompression) + # TODO: should we take focus by tabbing + self.setFocusPolicy(Qt.ClickFocus) + # XXX: make canvas font-agnostic as in defconAppkit and use + # glyph.getParent() instead + self.font = font + self.fetchFontMetrics() + self.glyphs = [] + self.ptSize = pointSize + self.calculateScale() + self.padding = 10 + self._editing = False + self._showKerning = False + self._showMetrics = False + self._verticalFlip = False + self._lineHeight = 1.1 + self._positions = None + self._selected = None + self.doubleClickCallback = None + self.pointSizeChangedCallback = None + self.selectionChangedCallback = None + + self._wrapLines = True + self._scrollArea = QScrollArea(self.parent()) + self._scrollArea.resizeEvent = self.resizeEvent + 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() + + def setLineHeight(self, lineHeight): + self._lineHeight = lineHeight + self.update() + + def setWrapLines(self, wrapLines): + if self._wrapLines == wrapLines: + return + self._wrapLines = wrapLines + if self._wrapLines: + self.resize(self._scrollArea.viewport().width(), self.height()) + self._scrollArea.setHorizontalScrollBarPolicy( + Qt.ScrollBarAlwaysOff) + self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + else: + self.resize(self.width(), self._scrollArea.viewport().height()) + 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 + self.descender = self.font.info.descender + 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 setSelected(self, selected): + self._selected = selected + if self._positions is not None: + cur_len = 0 + line = -1 + for index, li in enumerate(self._positions): + if cur_len + len(li) > self._selected: + pos, width = li[self._selected - cur_len] + line = index + break + cur_len += len(li) + if line > -1: + x = self.padding + pos + width / 2 + y = self.padding + (line + .5) * self.ptSize * self._lineHeight + self._scrollArea.ensureVisible( + x, y, width / 2 + 20, + .5 * self.ptSize * self._lineHeight + 20) + self.update() + + def resizeEvent(self, event): + if self._wrapLines: + self.resize(self._scrollArea.viewport().width(), self.height()) + else: + self.resize(self.width(), self._scrollArea.viewport().height()) + + def wheelEvent(self, event): + if event.modifiers() & Qt.ControlModifier: + # TODO: should it snap to predefined pointSizes? + # is the scaling factor okay? + # XXX: current alg. is not reversible... + decay = event.angleDelta().y() / 120.0 + scale = round(self.ptSize / 10) + if scale == 0 and decay >= 0: + scale = 1 + newPointSize = self.ptSize + int(decay) * scale + if newPointSize <= 0: + return + + self.setPointSize(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 + groups = self.font.groups + # quickly check to see if the pair is in the kerning dictionary + pair = (first, second) + if pair in kerning: + return kerning[pair] + # get group names and make sure first and second are glyph names + firstGroup = secondGroup = None + if first.startswith("@MMK_L"): + firstGroup = first + first = None + else: + for group, groupMembers in groups.items(): + if group.startswith("@MMK_L"): + if first in groupMembers: + firstGroup = group + break + if second.startswith("@MMK_R"): + secondGroup = second + second = None + else: + for group, groupMembers in groups.items(): + if group.startswith("@MMK_R"): + if second in groupMembers: + secondGroup = group + break + # make an ordered list of pairs to look up + pairs = [ + (first, second), + (first, secondGroup), + (firstGroup, second), + (firstGroup, secondGroup) + ] + # look up the pairs and return any matches + for pair in pairs: + if pair in kerning: + return kerning[pair] + return 0 + + def _arrowKeyPressEvent(self, event): + key = event.key() + modifiers = event.modifiers() + self._editing = True + if self._selected is not None: + glyph = self.glyphs[self._selected] + # TODO: not really DRY w other widgets + delta = event.count() + if modifiers & Qt.ShiftModifier: + delta *= 10 + if modifiers & Qt.ControlModifier: + delta *= 10 + if key == Qt.Key_Left: + delta = -delta + if modifiers & Qt.AltModifier: + if glyph.leftMargin is not None: + glyph.leftMargin += delta + else: + glyph.width += delta + self._editing = False + event.accept() + + def keyPressEvent(self, event): + key = event.key() + if key in arrowKeys: + self._arrowKeyPressEvent(event) + else: + super().keyPressEvent(event) + + def mousePressEvent(self, event): + if event.button() == Qt.LeftButton: + # XXX: investigate, baselineShift is unused + # if self._verticalFlip: + # baselineShift = -self.descender + # else: + # baselineShift = self.ascender + found = False + line = \ + (event.y() - self.padding) // (self.ptSize * self._lineHeight) + # 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) + event.accept() + self.update() + return + x = event.x() - self.padding + for index, data in enumerate(self._positions[line]): + pos, width = data + if pos <= x and pos + width > x: + count = 0 + for i in range(line): + count += len(self._positions[i]) + self._selected = count + index + found = True + break + if not found: + self._selected = None + if self.selectionChangedCallback is not None: + self.selectionChangedCallback(self._selected) + event.accept() + self.update() + # restore focus to ourselves, the table widget did take it when we + # sent notification + # TODO: maybe not set focus on notifiee instead + self.setFocus(Qt.MouseFocusReason) + else: + super(GlyphsCanvas, 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]) + else: + super(GlyphsCanvas, self).mouseDoubleClickEvent(event) + + def paintEvent(self, event): + linePen = QPen(Qt.black) + linePen.setWidth(3) + width = self.width() / self.scale + + def paintLineMarks(painter): + painter.save() + painter.scale(self.scale, yDirection * self.scale) + painter.setPen(linePen) + painter.drawLine(0, self.ascender, width, self.ascender) + 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) + if self._verticalFlip: + baselineShift = -self.descender + yDirection = 1 + else: + baselineShift = self.ascender + yDirection = -1 + painter.translate(self.padding, self.padding + + baselineShift * self.scale * self._lineHeight) + # TODO: scale painter here to avoid g*scale everywhere below + + cur_width = 0 + lines = 1 + self._positions = [[]] + if self._showMetrics: + paintLineMarks(painter) + for index, glyph in enumerate(self.glyphs): + # line wrapping + gWidth = glyph.width * self.scale + doKern = index > 0 and self._showKerning and cur_width > 0 + if doKern: + 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()) or glyph.unicode == 2029: + painter.translate(-cur_width, self.ptSize * self._lineHeight) + if self._showMetrics: + paintLineMarks(painter) + self._positions.append([(0, gWidth)]) + cur_width = gWidth + lines += 1 + else: + if doKern: + painter.translate(kern, 0) + self._positions[-1].append((cur_width, gWidth)) + cur_width += gWidth + kern + glyphPath = glyph.getRepresentation("defconQt.QPainterPath") + painter.save() + painter.scale(self.scale, yDirection * self.scale) + if self._showMetrics: + halfDescent = self.descender / 2 + painter.drawLine(0, 0, 0, halfDescent) + painter.drawLine(glyph.width, 0, glyph.width, halfDescent) + if self._selected is not None and index == self._selected: + painter.fillRect(0, self.descender, glyph.width, + self.upm, glyphSelectionColor) + painter.fillPath(glyphPath, Qt.black) + painter.restore() + painter.translate(gWidth, 0) + + innerHeight = self._scrollArea.viewport().height() + if not self._wrapLines: + innerWidth = self._scrollArea.viewport().width() + width = max(innerWidth, cur_width + self.padding * 2) + else: + width = self.width() + self.resize(width, max(innerHeight, lines * self.ptSize * + self._lineHeight + 2 * self.padding)) + + +class SpaceTableWidgetItem(QTableWidgetItem): + + def setData(self, role, value): + if role & Qt.EditRole: + # don't set empty data + # XXX: maybe fetch the value from cell back to the editor + if value == "": + return + super(SpaceTableWidgetItem, self).setData(role, value) + + +class GlyphCellItemDelegate(QStyledItemDelegate): + + def createEditor(self, parent, option, index): + editor = super(GlyphCellItemDelegate, self).createEditor( + parent, option, index) + # 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. + # TODO: cycle b/w editable cell area + def eventFilter(self, editor, event): + if event.type() == QEvent.KeyPress: + chg = None + count = event.count() + key = event.key() + if key == Qt.Key_Up: + chg = count + elif key == Qt.Key_Down: + chg = -count + elif not key == Qt.Key_Return: + return False + if chg is not None: + modifiers = event.modifiers() + if modifiers & Qt.AltModifier: + return False + elif modifiers & Qt.ShiftModifier: + chg *= 10 + if modifiers & Qt.ControlModifier: + chg *= 10 + cur = int(editor.text()) + editor.setText(str(cur + chg)) + self.commitData.emit(editor) + editor.selectAll() + return True + return False + + +class SpaceTable(QTableWidget): + + def __init__(self, parent=None): + super(SpaceTable, self).__init__(4, 1, parent) + self.setAttribute(Qt.WA_KeyCompression) + self.setItemDelegate(GlyphCellItemDelegate(self)) + data = [None, "Width", "Left", "Right"] + # Don't grey-out disabled cells + palette = self.palette() + fgColor = palette.color(QPalette.Text) + palette.setColor(QPalette.Disabled, QPalette.Text, fgColor) + self.setPalette(palette) + for index, title in enumerate(data): + item = SpaceTableWidgetItem(title) + item.setFlags(Qt.NoItemFlags) + self.setItem(index, 0, item) + # let's use this one column to compute the width of others + self._cellWidth = .5 * self.columnWidth(0) + self.setColumnWidth(0, self._cellWidth) + self.horizontalHeader().hide() + self.verticalHeader().hide() + self._coloredColumn = None + + # always show a scrollbar to fix layout + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.setSizePolicy(QSizePolicy( + QSizePolicy.Preferred, QSizePolicy.Fixed)) + self.glyphs = [] + self.fillGlyphs() + self.resizeRowsToContents() + self.currentItemChanged.connect(self._itemChanged) + self.cellChanged.connect(self._cellEdited) + self.setSelectionMode(QAbstractItemView.SingleSelection) + # edit cell on single click, not double + self.setEditTriggers(QAbstractItemView.CurrentChanged) + self._editing = False + self.selectionChangedCallback = 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, keepColor=False): + self.blockSignals(True) + self.setEditTriggers(QAbstractItemView.NoEditTriggers) + coloredColumn = self._coloredColumn + self.fillGlyphs() + if keepColor and coloredColumn is not None and \ + coloredColumn < self.columnCount(): + self.colorColumn(coloredColumn) + self.setEditTriggers(QAbstractItemView.CurrentChanged) + self.blockSignals(False) + + def _cellEdited(self, row, col): + if row == 0 or col == 0: + return + item = self.item(row, col).text() + # Glyphs that do not have outlines leave empty cells, can't convert + # that to a scalar + if not item: + return + item = int(item) + # -1 because the first col contains descriptive text + glyph = self.glyphs[col - 1] + # != comparisons avoid making glyph dirty when editor content is + # unchanged + self._editing = True + if row == 1: + if item != glyph.width: + glyph.width = item + elif row == 2: + if item != glyph.leftMargin: + glyph.leftMargin = item + elif row == 3: + if item != glyph.rightMargin: + glyph.rightMargin = item + self._editing = False + # defcon callbacks do the update + + def _itemChanged(self, current, previous): + if current is not None: + cur = current.column() + if previous is not None: + prev = previous.column() + 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 current is not None: + self.selectionChangedCallback(cur - 1) + else: + self.selectionChangedCallback(None) + + def colorColumn(self, column): + emptyBrush = QBrush(Qt.NoBrush) + selectionColor = QColor(235, 235, 235) + for i in range(4): + if self._coloredColumn is not None: + item = self.item(i, self._coloredColumn) + # cached column might be invalid if user input deleted it + if item is not None: + item.setBackground(emptyBrush) + if column is not None: + self.item(i, column).setBackground(selectionColor) + self._coloredColumn = column + + def sizeHint(self): + # http://stackoverflow.com/a/7216486/2037879 + height = sum(self.rowHeight(k) for k in range(self.rowCount())) + height += self.horizontalScrollBar().sizeHint().height() + margins = self.contentsMargins() + height += margins.top() + margins.bottom() + return QSize(self.width(), height) + + def setCurrentGlyph(self, glyphIndex): + self.blockSignals(True) + if glyphIndex is not None: + # so we can scroll to the item + self.setCurrentCell(1, glyphIndex + 1) + self.setCurrentItem(None) + if glyphIndex is not None: + self.colorColumn(glyphIndex + 1) + else: + self.colorColumn(glyphIndex) + self.blockSignals(False) + + def fillGlyphs(self): + def glyphTableWidgetItem(content, disableCell=False): + if isinstance(content, float): + content = round(content) + if content is not None: + content = str(content) + item = SpaceTableWidgetItem(content) + if disableCell: + item.setFlags(Qt.NoItemFlags) + elif content is None: + item.setFlags(Qt.ItemIsEnabled) + # TODO: should fields be centered? I find left-aligned more + # natural to read, personally... + # item.setTextAlignment(Qt.AlignCenter) + return item + + self._coloredColumn = None + self.setColumnCount(len(self.glyphs) + 1) + for index, glyph in enumerate(self.glyphs): + # TODO: see about allowing glyph name edit here + self.setItem(0, index + 1, glyphTableWidgetItem(glyph.name, True)) + self.setItem(1, index + 1, glyphTableWidgetItem(glyph.width)) + 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): + # A mouse can only scroll along the y-axis. Use x-axis if we have one + # (e.g. from touchpad), otherwise use y-axis. + angleDelta = event.angleDelta().x() or event.angleDelta().y() + cur = self.horizontalScrollBar().value() + self.horizontalScrollBar().setValue(cur - angleDelta / 120) + event.accept() diff --git a/Lib/defconQt/spaceCenter.py b/Lib/defconQt/spaceCenter.py deleted file mode 100644 index e360653..0000000 --- a/Lib/defconQt/spaceCenter.py +++ /dev/null @@ -1,826 +0,0 @@ -from defconQt import icons_db # noqa -from defconQt.glyphCollectionView import cellSelectionColor -from defconQt.glyphView import MainGfxWindow -from getpass import getuser -from PyQt5.QtCore import QEvent, QSettings, QSize, Qt -from PyQt5.QtGui import ( - QBrush, QColor, QIcon, QIntValidator, QKeySequence, QPainter, QPalette, - QPen) -from PyQt5.QtWidgets import ( - QAbstractItemView, QActionGroup, QApplication, QComboBox, QLineEdit, QMenu, - QPushButton, QScrollArea, QStyledItemDelegate, QTableWidget, - QTableWidgetItem, QVBoxLayout, QSizePolicy, QToolBar, QWidget) - -comboBoxItems = [ - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "0123456789", - "nn/? nono/? oo", - "HH/? HOHO/? OO", -] - -defaultPointSize = 150 -glyphSelectionColor = QColor(cellSelectionColor) -glyphSelectionColor.setAlphaF(.09) - - -class MainSpaceWindow(QWidget): - - def __init__(self, font, string=None, pointSize=defaultPointSize, - parent=None): - super(MainSpaceWindow, self).__init__(parent, Qt.Window) - - if string is None: - try: - string = getuser() - except: - string = "World" - string = "Hello %s" % string - # TODO: drop self.font and self.glyphs, store in the widgets only - self.font = font - self.glyphs = [] - self.toolbar = FontToolBar(pointSize, self) - self.canvas = GlyphsCanvas(font, pointSize, self) - self.table = SpaceTable(self) - self.toolbar.comboBox.currentIndexChanged[ - str].connect(self.canvas.setPointSize) - self.canvas.doubleClickCallback = self._glyphOpened - self.canvas.pointSizeChangedCallback = self.toolbar.setPointSize - self.canvas.selectionChangedCallback = self.table.setCurrentGlyph - self.table.selectionChangedCallback = self.canvas.setSelected - - self.toolbar.textField.editTextChanged.connect(self._textChanged) - self.toolbar.textField.setEditText(string) - app = QApplication.instance() - app.currentGlyphChanged.connect(self._textChanged) - - layout = QVBoxLayout(self) - layout.addWidget(self.toolbar) - layout.addWidget(self.canvas.scrollArea()) - layout.addWidget(self.table) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - self.setLayout(layout) - self.resize(600, 500) - - self.font.info.addObserver(self, "_fontInfoChanged", "Info.Changed") - - self.setWindowTitle("Space center – %s %s" % ( - self.font.info.familyName, self.font.info.styleName)) - - def setupFileMenu(self): - fileMenu = QMenu("&File", self) - fileMenu.addAction("&Save...", self.save, QKeySequence.Save) - fileMenu.addAction("E&xit", self.close, QKeySequence.Quit) - self.menuBar().addMenu(fileMenu) - - 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(glyph, self.parent()) - glyphViewWindow.show() - - def _textChanged(self): - # unsubscribe from the old glyphs - self._unsubscribeFromGlyphs() - # subscribe to the new glyphs - left = self.textToGlyphNames(self.toolbar.leftTextField.text()) - newText = self.textToGlyphNames(self.toolbar.textField.currentText()) - right = self.textToGlyphNames(self.toolbar.rightTextField.text()) - leftGlyphs = [] - for name in left: - if name in self.font: - leftGlyphs.append(self.font[name]) - rightGlyphs = [] - for name in right: - if name in self.font: - rightGlyphs.append(self.font[name]) - finalGlyphs = [] - for name in newText: - if name in self.font: - finalGlyphs.extend(leftGlyphs) - finalGlyphs.append(self.font[name]) - finalGlyphs.extend(rightGlyphs) - self._subscribeToGlyphs(finalGlyphs) - # set the records into the view - self.canvas.setGlyphs(self.glyphs) - self.table.setGlyphs(self.glyphs) - - # Tal Leming. Edited. - def textToGlyphNames(self, text): - def catchCompile(): - if compileStack[0] == "?": - glyph = app.currentGlyph() - if glyph is not None: - glyphNames.append(glyph.name) - elif compileStack: - glyphNames.append("".join(compileStack)) - - app = QApplication.instance() - # escape // - text = text.replace("//", "/slash ") - # - glyphNames = [] - compileStack = None - for c in text: - # start a glyph name compile. - if c == "/": - # finishing a previous compile. - if compileStack is not None: - # only add the compile if something has been added to the - # stack. - if compileStack: - glyphNames.append("".join(compileStack)) - # reset the stack. - compileStack = [] - # adding to or ending a glyph name compile. - elif compileStack is not None: - # space. conclude the glyph name compile. - if c == " ": - # only add the compile if something has been added to the - # stack. - catchCompile() - compileStack = None - # add the character to the stack. - else: - compileStack.append(c) - # adding a character that needs to be converted to a glyph name. - else: - glyphName = self.font.unicodeData.glyphNameForUnicode(ord(c)) - glyphNames.append(glyphName) - # catch remaining compile. - if compileStack is not None and compileStack: - catchCompile() - return glyphNames - - def _subscribeToGlyphs(self, glyphs): - self.glyphs = glyphs - - handledGlyphs = set() - for glyph in self.glyphs: - if glyph in handledGlyphs: - continue - handledGlyphs.add(glyph) - glyph.addObserver(self, "_glyphChanged", "Glyph.Changed") - - def _unsubscribeFromGlyphs(self): - handledGlyphs = set() - for glyph in self.glyphs: - if glyph in handledGlyphs: - continue - handledGlyphs.add(glyph) - glyph.removeObserver(self, "Glyph.Changed") - # self.glyphs = None - - def setGlyphs(self, glyphs): - # unsubscribe from the old glyphs - self._unsubscribeFromGlyphs() - # subscribe to the new glyphs - self._subscribeToGlyphs(glyphs) - glyphNames = [] - for glyph in glyphs: - if glyph.unicode: - glyphNames.append(chr(glyph.unicode)) - else: - glyphNames.append("".join(("/", glyph.name, " "))) - self.toolbar.textField.setEditText("".join(glyphNames)) - # set the records into the view - self.canvas.setGlyphs(self.glyphs) - self.table.setGlyphs(self.glyphs) - -pointSizes = [50, 75, 100, 125, 150, 200, 250, 300, 350, 400, 450, 500] - - -class FontToolBar(QToolBar): - - def __init__(self, pointSize, parent=None): - super(FontToolBar, self).__init__(parent) - auxiliaryWidth = self.fontMetrics().width('0') * 8 - self.leftTextField = QLineEdit(self) - self.leftTextField.setMaximumWidth(auxiliaryWidth) - self.textField = QComboBox(self) - self.textField.setEditable(True) - completer = self.textField.completer() - completer.setCaseSensitivity(Qt.CaseSensitive) - self.textField.setCompleter(completer) - # XXX: had to use Maximum because Preferred did entend the widget(?) - self.textField.setSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Maximum) - items = QSettings().value("spaceCenter/comboBoxItems", comboBoxItems, - str) - self.textField.addItems(items) - self.rightTextField = QLineEdit(self) - self.rightTextField.setMaximumWidth(auxiliaryWidth) - self.leftTextField.textEdited.connect(self.textField.editTextChanged) - self.rightTextField.textEdited.connect(self.textField.editTextChanged) - self.comboBox = QComboBox(self) - self.comboBox.setEditable(True) - self.comboBox.setValidator(QIntValidator(self)) - for p in pointSizes: - self.comboBox.addItem(str(p)) - self.comboBox.setEditText(str(pointSize)) - - self.configBar = QPushButton(self) - self.configBar.setFlat(True) - self.configBar.setIcon(QIcon(":/resources/settings.svg")) - self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px") - self.toolsMenu = QMenu(self) - showKerning = self.toolsMenu.addAction( - "Show Kerning", self.showKerning) - showKerning.setCheckable(True) - showMetrics = self.toolsMenu.addAction( - "Show Metrics", self.showMetrics) - showMetrics.setCheckable(True) - self.toolsMenu.addSeparator() - wrapLines = self.toolsMenu.addAction("Wrap lines", self.wrapLines) - wrapLines.setCheckable(True) - noWrapLines = self.toolsMenu.addAction("No wrap", self.noWrapLines) - noWrapLines.setCheckable(True) - self.toolsMenu.addSeparator() - verticalFlip = self.toolsMenu.addAction( - "Vertical flip", self.verticalFlip) - verticalFlip.setCheckable(True) - """ - lineHeight = QWidgetAction(self.toolsMenu) - lineHeight.setText("Line height:") - lineHeightSlider = QSlider(Qt.Horizontal, self) - # QSlider works with integers so we'll just divide by 100 what comes - # out of it - lineHeightSlider.setMinimum(80) - lineHeightSlider.setMaximum(160) - lineHeightSlider.setValue(100) - #lineHeightSlider.setContentsMargins(30, 0, 30, 0) - lineHeightSlider.valueChanged.connect(self.lineHeight) - lineHeight.setDefaultWidget(lineHeightSlider) - self.toolsMenu.addAction(lineHeight) - """ - - wrapLinesGroup = QActionGroup(self) - wrapLinesGroup.addAction(wrapLines) - wrapLinesGroup.addAction(noWrapLines) - wrapLines.setChecked(True) - # self.toolsMenu.setActiveAction(wrapLines) - self.configBar.setMenu(self.toolsMenu) - - self.addWidget(self.leftTextField) - self.addWidget(self.textField) - self.addWidget(self.rightTextField) - self.addWidget(self.comboBox) - self.addWidget(self.configBar) - - def showEvent(self, event): - super(FontToolBar, self).showEvent(event) - self.textField.setFocus(True) - - 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 lineHeight(self, value): - self.parent().canvas.setLineHeight(value / 100) - - def wrapLines(self): - self.parent().canvas.setWrapLines(True) - - def noWrapLines(self): - self.parent().canvas.setWrapLines(False) - - -class GlyphsCanvas(QWidget): - - def __init__(self, font, 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 = [] - self.ptSize = pointSize - self.calculateScale() - self.padding = 10 - self._showKerning = False - self._showMetrics = False - self._verticalFlip = False - self._lineHeight = 1.1 - self._positions = None - self._selected = None - self.doubleClickCallback = None - self.pointSizeChangedCallback = None - self.selectionChangedCallback = None - - self._wrapLines = True - self._scrollArea = QScrollArea(self.parent()) - self._scrollArea.resizeEvent = self.resizeEvent - 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() - - def setLineHeight(self, lineHeight): - self._lineHeight = lineHeight - self.update() - - def setWrapLines(self, wrapLines): - if self._wrapLines == wrapLines: - return - self._wrapLines = wrapLines - if self._wrapLines: - self.resize(self._scrollArea.viewport().width(), self.height()) - self._scrollArea.setHorizontalScrollBarPolicy( - Qt.ScrollBarAlwaysOff) - self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - else: - self.resize(self.width(), self._scrollArea.viewport().height()) - 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 - self.descender = self.font.info.descender - 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 setSelected(self, selected): - self._selected = selected - if self._positions is not None: - cur_len = 0 - line = -1 - for index, li in enumerate(self._positions): - if cur_len + len(li) > self._selected: - pos, width = li[self._selected - cur_len] - line = index - break - cur_len += len(li) - if line > -1: - x = self.padding + pos + width / 2 - y = self.padding + (line + .5) * self.ptSize * self._lineHeight - self._scrollArea.ensureVisible( - x, y, width / 2 + 20, - .5 * self.ptSize * self._lineHeight + 20) - self.update() - - def resizeEvent(self, event): - if self._wrapLines: - self.resize(self._scrollArea.viewport().width(), self.height()) - else: - self.resize(self.width(), self._scrollArea.viewport().height()) - - def wheelEvent(self, event): - if event.modifiers() & Qt.ControlModifier: - # TODO: should it snap to predefined pointSizes? - # is the scaling factor okay? - # XXX: current alg. is not reversible... - decay = event.angleDelta().y() / 120.0 - scale = round(self.ptSize / 10) - if scale == 0 and decay >= 0: - scale = 1 - newPointSize = self.ptSize + int(decay) * scale - if newPointSize <= 0: - return - - self.setPointSize(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 - groups = self.font.groups - # quickly check to see if the pair is in the kerning dictionary - pair = (first, second) - if pair in kerning: - return kerning[pair] - # get group names and make sure first and second are glyph names - firstGroup = secondGroup = None - if first.startswith("@MMK_L"): - firstGroup = first - first = None - else: - for group, groupMembers in groups.items(): - if group.startswith("@MMK_L"): - if first in groupMembers: - firstGroup = group - break - if second.startswith("@MMK_R"): - secondGroup = second - second = None - else: - for group, groupMembers in groups.items(): - if group.startswith("@MMK_R"): - if second in groupMembers: - secondGroup = group - break - # make an ordered list of pairs to look up - pairs = [ - (first, second), - (first, secondGroup), - (firstGroup, second), - (firstGroup, secondGroup) - ] - # look up the pairs and return any matches - for pair in pairs: - 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... - self.setFocus(Qt.MouseFocusReason) - if event.button() == Qt.LeftButton: - # XXX: investigate, baselineShift is unused - # if self._verticalFlip: - # baselineShift = -self.descender - # else: - # baselineShift = self.ascender - found = False - line = \ - (event.y() - self.padding) // (self.ptSize * self._lineHeight) - # 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) - event.accept() - self.update() - return - x = event.x() - self.padding - for index, data in enumerate(self._positions[line]): - pos, width = data - if pos <= x and pos + width > x: - count = 0 - for i in range(line): - count += len(self._positions[i]) - self._selected = count + index - found = True - break - if not found: - self._selected = None - if self.selectionChangedCallback is not None: - self.selectionChangedCallback(self._selected) - event.accept() - self.update() - else: - super(GlyphsCanvas, 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]) - else: - super(GlyphsCanvas, self).mouseDoubleClickEvent(event) - - def paintEvent(self, event): - linePen = QPen(Qt.black) - linePen.setWidth(3) - width = self.width() / self.scale - - def paintLineMarks(painter): - painter.save() - painter.scale(self.scale, yDirection * self.scale) - painter.setPen(linePen) - painter.drawLine(0, self.ascender, width, self.ascender) - 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) - if self._verticalFlip: - baselineShift = -self.descender - yDirection = 1 - else: - baselineShift = self.ascender - yDirection = -1 - painter.translate(self.padding, self.padding + - baselineShift * self.scale * self._lineHeight) - # TODO: scale painter here to avoid g*scale everywhere below - - cur_width = 0 - lines = 1 - self._positions = [[]] - if self._showMetrics: - paintLineMarks(painter) - for index, glyph in enumerate(self.glyphs): - # line wrapping - gWidth = glyph.width * self.scale - doKern = index > 0 and self._showKerning and cur_width > 0 - if doKern: - 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) - if self._showMetrics: - paintLineMarks(painter) - self._positions.append([(0, gWidth)]) - cur_width = gWidth - lines += 1 - else: - if doKern: - painter.translate(kern, 0) - self._positions[-1].append((cur_width, gWidth)) - cur_width += gWidth + kern - glyphPath = glyph.getRepresentation("defconQt.QPainterPath") - painter.save() - painter.scale(self.scale, yDirection * self.scale) - if self._showMetrics: - halfDescent = self.descender / 2 - painter.drawLine(0, 0, 0, halfDescent) - painter.drawLine(glyph.width, 0, glyph.width, halfDescent) - if self._selected is not None and index == self._selected: - painter.fillRect(0, self.descender, glyph.width, - self.upm, glyphSelectionColor) - painter.fillPath(glyphPath, Qt.black) - painter.restore() - painter.translate(gWidth, 0) - - innerHeight = self._scrollArea.viewport().height() - if not self._wrapLines: - innerWidth = self._scrollArea.viewport().width() - width = max(innerWidth, cur_width + self.padding * 2) - else: - width = self.width() - self.resize(width, max(innerHeight, lines * self.ptSize * - self._lineHeight + 2 * self.padding)) - - -class SpaceTableWidgetItem(QTableWidgetItem): - - def setData(self, role, value): - if role & Qt.EditRole: - # don't set empty data - # XXX: maybe fetch the value from cell back to the editor - if value == "": - return - super(SpaceTableWidgetItem, self).setData(role, value) - - -class GlyphCellItemDelegate(QStyledItemDelegate): - - def createEditor(self, parent, option, index): - editor = super(GlyphCellItemDelegate, self).createEditor( - parent, option, index) - # 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. - # TODO: cycle b/w editable cell area - def eventFilter(self, editor, event): - if event.type() == QEvent.KeyPress: - chg = None - count = event.count() - key = event.key() - if key == Qt.Key_Up: - chg = count - elif key == Qt.Key_Down: - chg = -count - elif not key == Qt.Key_Return: - return False - if chg is not None: - modifiers = event.modifiers() - if modifiers & Qt.AltModifier: - return False - elif modifiers & Qt.ShiftModifier: - chg *= 10 - if modifiers & Qt.ControlModifier: - chg *= 10 - cur = int(editor.text()) - editor.setText(str(cur + chg)) - self.commitData.emit(editor) - editor.selectAll() - return True - return False - - -class SpaceTable(QTableWidget): - - def __init__(self, parent=None): - super(SpaceTable, self).__init__(4, 1, parent) - self.setAttribute(Qt.WA_KeyCompression) - self.setItemDelegate(GlyphCellItemDelegate(self)) - data = [None, "Width", "Left", "Right"] - # Don't grey-out disabled cells - palette = self.palette() - fgColor = palette.color(QPalette.Text) - palette.setColor(QPalette.Disabled, QPalette.Text, fgColor) - self.setPalette(palette) - for index, title in enumerate(data): - item = SpaceTableWidgetItem(title) - item.setFlags(Qt.NoItemFlags) - self.setItem(index, 0, item) - # let's use this one column to compute the width of others - self._cellWidth = .5 * self.columnWidth(0) - self.setColumnWidth(0, self._cellWidth) - self.horizontalHeader().hide() - self.verticalHeader().hide() - - # always show a scrollbar to fix layout - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - self.setSizePolicy(QSizePolicy( - QSizePolicy.Preferred, QSizePolicy.Fixed)) - self.glyphs = [] - self.fillGlyphs() - self.resizeRowsToContents() - self.currentItemChanged.connect(self._itemChanged) - self.cellChanged.connect(self._cellEdited) - self.setSelectionMode(QAbstractItemView.SingleSelection) - # edit cell on single click, not double - self.setEditTriggers(QAbstractItemView.CurrentChanged) - self._editing = False - 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): - self.blockSignals(True) - if self._editing: - coloredColumn = self._coloredColumn - self.fillGlyphs() - if self._editing: - self._coloredColumn = coloredColumn - self.blockSignals(False) - - def _cellEdited(self, row, col): - if row == 0 or col == 0: - return - item = self.item(row, col).text() - # Glyphs that do not have outlines leave empty cells, can't convert - # that to a scalar - if not item: - return - item = int(item) - # -1 because the first col contains descriptive text - glyph = self.glyphs[col - 1] - # != comparisons avoid making glyph dirty when editor content is - # unchanged - self._editing = True - if row == 1: - if item != glyph.width: - glyph.width = item - elif row == 2: - if item != glyph.leftMargin: - glyph.leftMargin = item - elif row == 3: - if item != glyph.rightMargin: - glyph.rightMargin = item - self._editing = False - # defcon callbacks do the update - - def _itemChanged(self, current, previous): - if current is not None: - cur = current.column() - if previous is not None: - prev = previous.column() - 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 current is not None: - self.selectionChangedCallback(cur - 1) - else: - self.selectionChangedCallback(None) - - def colorColumn(self, column): - emptyBrush = QBrush(Qt.NoBrush) - selectionColor = QColor(235, 235, 235) - for i in range(4): - if self._coloredColumn is not None: - item = self.item(i, self._coloredColumn) - # cached column might be invalid if user input deleted it - if item is not None: - item.setBackground(emptyBrush) - if column is not None: - self.item(i, column).setBackground(selectionColor) - self._coloredColumn = column - - def sizeHint(self): - # http://stackoverflow.com/a/7216486/2037879 - height = sum(self.rowHeight(k) for k in range(self.rowCount())) - height += self.horizontalScrollBar().sizeHint().height() - margins = self.contentsMargins() - height += margins.top() + margins.bottom() - return QSize(self.width(), height) - - def setCurrentGlyph(self, glyphIndex): - self.blockSignals(True) - if glyphIndex is not None: - # so we can scroll to the item - self.setCurrentCell(1, glyphIndex + 1) - self.setCurrentItem(None) - if glyphIndex is not None: - self.colorColumn(glyphIndex + 1) - else: - self.colorColumn(glyphIndex) - self.blockSignals(False) - - def fillGlyphs(self): - def glyphTableWidgetItem(content, disableCell=False): - if isinstance(content, float): - content = round(content) - if content is not None: - content = str(content) - item = SpaceTableWidgetItem(content) - if disableCell: - item.setFlags(Qt.NoItemFlags) - elif content is None: - item.setFlags(Qt.ItemIsEnabled) - # TODO: should fields be centered? I find left-aligned more - # natural to read, personally... - # item.setTextAlignment(Qt.AlignCenter) - return item - - self.setColumnCount(len(self.glyphs) + 1) - for index, glyph in enumerate(self.glyphs): - # TODO: see about allowing glyph name edit here - self.setItem(0, index + 1, glyphTableWidgetItem(glyph.name, True)) - self.setItem(1, index + 1, glyphTableWidgetItem(glyph.width)) - 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): - # A mouse can only scroll along the y-axis. Use x-axis if we have one - # (e.g. from touchpad), otherwise use y-axis. - angleDelta = event.angleDelta().x() or event.angleDelta().y() - cur = self.horizontalScrollBar().value() - self.horizontalScrollBar().setValue(cur - angleDelta / 120) - event.accept() -- cgit v1.2.3