Last active
February 14, 2022 18:49
-
-
Save okay-type/e8e608926dcbcef6148cfaadddb2e959 to your computer and use it in GitHub Desktop.
rf4 version of the Ramsay Street Extension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # menuTitle : Mr Rogers | |
| # shortCut : command+shift+k | |
| import merz | |
| from mojo.subscriber import Subscriber, registerGlyphEditorSubscriber, registerSubscriberEvent, getRegisteredSubscriberEvents, listRegisteredSubscribers, unregisterGlyphEditorSubscriber | |
| from mojo.UI import SetCurrentGlyphByName | |
| from fontTools.pens.basePen import BasePen | |
| from importlib import reload | |
| import MrRogersData | |
| reload(MrRogersData) | |
| from MrRogersData import neighborData | |
| drawInPreview = True | |
| ''' | |
| controls: | |
| shortcuts are editable in glyphEditorDidKeyDown, fyi | |
| ⌘+⇪+K activate/deactivate tool entirely | |
| ⌘+K toggle tool visibility | |
| ^K rotate preview through AllFonts | |
| ⌥K rotate preview through the list of pairs | |
| shift to rotate the other direction | |
| x3☟ triple click a component to go to that glyph | |
| tip: use previousCurrentGlyph.py to easily return | |
| https://gist.github.com/okay-type/5ff7c9a24162884680b06461a1352abb | |
| data: | |
| data is stored in a second file as a dictionary of lists of tuples: | |
| 'glyph': [('leftA', 'rightA'), ('leftB', 'rightB'), ...] | |
| updateNeighborData() automatically adds some control pairs to the list: | |
| (CurrentGlyph, CurrentGlyph) | |
| (H, H) | |
| (O, O) | |
| concept forked from typemytype's ramsayStreetRoboFontExtension | |
| ''' | |
| class MrRogers(Subscriber): | |
| debug = False | |
| def build(self): | |
| self.toggleOnOff = True | |
| self.clickable = {} | |
| self.glyphEditor = self.getGlyphEditor() | |
| self.backgroundContainer = self.glyphEditor.extensionContainer( | |
| identifier='com.robofont.drawNeighbors.background', | |
| location='background', | |
| clear=True | |
| ) | |
| self.leftNeighborLayer = self.backgroundContainer.appendPathSublayer( | |
| fillColor=(.3, 0, 1, .5), | |
| name='leftNeighbourLayer') | |
| self.rightNeighborLayer = self.backgroundContainer.appendPathSublayer( | |
| fillColor=(.3, 0, 1, .5), | |
| name='rightNeighborLayer') | |
| self.previewContainer = self.glyphEditor.extensionContainer( | |
| identifier='com.robofont.drawNeighbors.preview', | |
| location='preview', | |
| clear=True | |
| ) | |
| self.leftPreviewLayer = self.previewContainer.appendPathSublayer( | |
| fillColor=(0, 0, 0, 1), | |
| name='leftPreviewLayer') | |
| self.rightPreviewLayer = self.previewContainer.appendPathSublayer( | |
| fillColor=(0, 0, 0, 1), | |
| name='rightPreviewLayer') | |
| # all fonts | |
| self.neighborIndex = 0 | |
| self.updateFontList() | |
| self.allFontsActive = self.allFonts.index(CurrentFont()) | |
| self.neighborData = self.updateNeighborData(neighborData.neighbors()) | |
| def updateNeighborData(self, neighborData): | |
| f = CurrentFont() | |
| fontglyphs = f.keys() | |
| # add glyphs in font that aren't in dict | |
| for glyphName in fontglyphs: | |
| if glyphName not in neighborData: | |
| neighborData[glyphName] = [] | |
| # if they're suffixed, try to add a base glyphs | |
| if '.' in glyphName: | |
| gbase, gsuffix = glyphName.split('.', 1) | |
| if gbase in neighborData: | |
| for pair in neighborData[gbase]: | |
| spair = (pair[0]+'.'+gsuffix, pair[1]+'.'+gsuffix) | |
| if spair[0] in fontglyphs and spair[1] in fontglyphs: | |
| neighborData[glyphName].append(spair) | |
| # add basic control pairs to every glyph | |
| for glyphName in neighborData: | |
| neighborData[glyphName].append((glyphName, glyphName)) | |
| pairs = [ | |
| ('H', 'H'), | |
| ('O', 'O'), | |
| ('d', 'h'), | |
| ('o', 'o') | |
| ] | |
| for pair in pairs: | |
| if pair not in neighborData[glyphName]: | |
| neighborData[glyphName].append(pair) | |
| if '.' in glyphName: | |
| gbase, gsuffix = glyphName.split('.', 1) | |
| spair = (pair[0]+'.'+gsuffix, pair[1]+'.'+gsuffix) | |
| if spair[0] in fontglyphs and spair[1] in fontglyphs: | |
| neighborData[glyphName].append(spair) | |
| return neighborData | |
| def destroy(self): | |
| self.backgroundContainer.clearSublayers() | |
| self.previewContainer.clearSublayers() | |
| def glyphEditorDidSetGlyph(self, info): | |
| glyph = info['glyph'] | |
| if glyph is None: | |
| return | |
| if glyph.name in self.neighborData and self.neighborIndex > len(self.neighborData[glyph.name])-1: | |
| self.neighborIndex = 0 | |
| self.drawNeighbors(glyph) | |
| def glyphEditorGlyphDidChange(self, info): | |
| glyph = info['glyph'] | |
| self.drawNeighbors(glyph) | |
| def glyphEditorGlyphMetricsDidChange(self, info): | |
| glyph = info['glyph'] | |
| self.drawNeighbors(glyph) | |
| def glyphEditorDidKeyDown(self, info): | |
| deviceState = info['iterations'][0]['deviceState'] | |
| keyDown = deviceState['keyDownWithoutModifiers'] | |
| controlDown = deviceState['controlDown'] | |
| optionDown = deviceState['optionDown'] | |
| commandDown = deviceState['commandDown'] | |
| shiftDown = deviceState['shiftDown'] | |
| if keyDown == 'k' or keyDown == 'K': | |
| # toggle view on-off | |
| if commandDown != 0: | |
| self.toggleOnOff = not self.toggleOnOff | |
| if CurrentGlyph(): | |
| self.drawNeighbors(CurrentGlyph()) | |
| # rotate fonts | |
| if controlDown != 0: | |
| if shiftDown == 0: | |
| self.allFontsActive += 1 | |
| if self.allFontsActive > len(self.allFonts)-1: | |
| self.allFontsActive = 0 | |
| else: | |
| self.allFontsActive += -1 | |
| if self.allFontsActive < 0: | |
| self.allFontsActive = len(self.allFonts)-1 | |
| if CurrentGlyph(): | |
| self.drawNeighbors(CurrentGlyph()) | |
| # rotate fonts | |
| if optionDown != 0: | |
| if CurrentGlyph(): | |
| if CurrentGlyph().name in self.neighborData: | |
| if shiftDown == 0: | |
| self.neighborIndex += 1 | |
| if self.neighborIndex > len(self.neighborData[CurrentGlyph().name])-1: | |
| self.neighborIndex = 0 | |
| else: | |
| self.neighborIndex += -1 | |
| if self.neighborIndex < 0: | |
| self.neighborIndex = len(self.neighborData[CurrentGlyph().name])-1 | |
| self.drawNeighbors(CurrentGlyph()) | |
| def glyphEditorDidMouseDown(self, info): | |
| lle = info['lowLevelEvents'][0] | |
| point = lle['point'] | |
| cc = lle['clickCount'] | |
| if cc == 3: | |
| for componentKey in self.clickable: | |
| componentObject = self.clickable[componentKey] | |
| path = componentObject.naked().getRepresentation('defconAppKit.NSBezierPath') | |
| if path.containsPoint_(point): | |
| SetCurrentGlyphByName(componentKey.replace('_LLLL', '').replace('_RRRR', '')) | |
| break | |
| def fontDocumentDidOpen(self, info): | |
| self.updateFontList() | |
| def fontDocumentDidClose(self, info): | |
| self.updateFontList() | |
| def updateFontList(self): | |
| self.allFonts = AllFonts() | |
| if len(self.allFonts) > 1: | |
| weight_classes = [f.info.openTypeOS2WeightClass for f in self.allFonts] | |
| width_classes = [f.info.openTypeOS2WidthClass for f in self.allFonts] | |
| if all(weight_classes) and all(width_classes): | |
| self.allFonts = sorted(self.allFonts, key=lambda f: (f.info.openTypeOS2WeightClass, f.info.openTypeOS2WidthClass)) | |
| else: | |
| self.allFonts = AllFonts('styleName') | |
| def drawNeighbors(self, glyph): | |
| self.clickable = {} | |
| if glyph is None: | |
| return | |
| if self.toggleOnOff is False: | |
| self.leftNeighborLayer.setPath(None) | |
| self.rightNeighborLayer.setPath(None) | |
| self.leftPreviewLayer.setPath(None) | |
| self.rightPreviewLayer.setPath(None) | |
| return | |
| font = self.allFonts[self.allFontsActive] | |
| glyphName = glyph.name | |
| neighborGlyphs = ['H', 'H'] | |
| if glyphName in self.neighborData: | |
| neighborGlyphs = self.neighborData[glyphName][self.neighborIndex] | |
| neighborGlyphs = (self.ifGlyphInFont(neighborGlyphs[0], glyphName, font), self.ifGlyphInFont(neighborGlyphs[1], glyphName, font)) | |
| leftGlyph = font[neighborGlyphs[0]] | |
| rightGlyph = font[neighborGlyphs[1]] | |
| self.leftNeighborLayer.setPath(leftGlyph.getRepresentation('merz.CGPath')) | |
| self.leftNeighborLayer.setPosition((-leftGlyph.width, 0)) | |
| self.rightNeighborLayer.setPath(rightGlyph.getRepresentation('merz.CGPath')) | |
| self.rightNeighborLayer.setPosition((glyph.width, 0)) | |
| self.leftPreviewLayer.setPath(leftGlyph.getRepresentation('merz.CGPath')) | |
| self.leftPreviewLayer.setPosition((-leftGlyph.width, 0)) | |
| self.rightPreviewLayer.setPath(rightGlyph.getRepresentation('merz.CGPath')) | |
| self.rightPreviewLayer.setPosition((glyph.width, 0)) | |
| leftGlyphCopy = leftGlyph.copy() | |
| leftGlyphCopy.moveBy((-leftGlyph.width, 0)) | |
| self.clickable[leftGlyph.name+'_LLLL'] = leftGlyphCopy | |
| rightGlyphCopy = rightGlyph.copy() | |
| rightGlyphCopy.moveBy((glyph.width, 0)) | |
| self.clickable[rightGlyph.name+'_RRRR'] = rightGlyphCopy | |
| def ifGlyphInFont(self, g, fallback, font): | |
| if g in font.glyphOrder: | |
| return g | |
| else: | |
| return fallback | |
| if __name__ == '__main__': | |
| registered_subscribers = listRegisteredSubscribers(subscriberClassName='MrRogers') | |
| if len(registered_subscribers) > 0: | |
| for target_subscriber in registered_subscribers: | |
| unregisterGlyphEditorSubscriber(target_subscriber) | |
| print('Toggle Mr. Rogers off') | |
| else: | |
| registerGlyphEditorSubscriber(MrRogers) | |
| print('Toggle Mr. Rogers on') | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class neighborData(): | |
| def neighbors(): | |
| return { | |
| 'A': [('H', 'V')], | |
| 'C': [('c', 'G')], | |
| 'B': [('P', 'D')], | |
| 'E': [('B', 'F')], | |
| 'D': [('B', 'P')], | |
| 'G': [('C', 'O')], | |
| 'F': [('P', 'E')], | |
| 'I': [('J', 'H')], | |
| 'H': [('I', 'P')], | |
| 'K': [('k', 'I')], | |
| 'J': [('j', 'I')], | |
| 'M': [('H', 'N')], | |
| 'L': [('I', 'H')], | |
| 'O': [('C', 'o')], | |
| 'N': [('M', 'V')], | |
| 'Q': [('O', 'G')], | |
| 'P': [('R', 'p')], | |
| 'S': [('C', 's')], | |
| 'R': [('B', 'P')], | |
| 'U': [('u', 'H')], | |
| 'T': [('I', 'H')], | |
| 'W': [('w', 'V')], | |
| 'V': [('v', 'W')], | |
| 'Y': [('y', 'V')], | |
| 'X': [('x', 'Y')], | |
| 'Z': [('z', 'X')], | |
| 'a': [('n', 'e')], | |
| 'c': [('e', 'C')], | |
| 'b': [('d', 'p')], | |
| 'e': [('o', 'c')], | |
| 'd': [('q', 'b')], | |
| 'g': [('o', 'q')], | |
| 'f': [('i', 't')], | |
| 'i': [('period', 'j')], | |
| 'h': [('l', 'n')], | |
| 'k': [('h', 'K')], | |
| 'j': [('i', 'period')], | |
| 'm': [('n', 'w')], | |
| 'l': [('h', 'k')], | |
| 'o': [('c', 'O')], | |
| 'n': [('h', 'm')], | |
| 'q': [('d', 'p')], | |
| 'p': [('q', 'P')], | |
| 's': [('e', 'S')], | |
| 'r': [('s', 'n')], | |
| 'u': [('v', 'n')], | |
| 't': [('s', 'f')], | |
| 'w': [('v', 'W')], | |
| 'v': [('u', 'w')], | |
| 'y': [('v', 'Y')], | |
| 'x': [('y', 'X')], | |
| 'z': [('x', 'Z') | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment