Last active
May 22, 2023 19:52
-
-
Save typemytype/45293f9b29f29cd2458157a44f8fad94 to your computer and use it in GitHub Desktop.
This file contains 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 : Headless RoboFont | |
import os | |
import AppKit | |
import ezui | |
from mojo.extensions import getExtensionDefault, setExtensionDefault | |
from mojo.subscriber import Subscriber, registerRoboFontSubscriber | |
from mojo.UI import AskString, OpenGlyphWindow, OpenSpaceCenter, OpenFontInfoSheet | |
defaults_key = 'com.typemytype.headLessRoboFont.glyphName' | |
class OpenGlyphController(ezui.WindowController): | |
def build(self, parent, fonts): | |
self.fonts = fonts | |
content = """ | |
Open Glyph: [__] @glyphName | |
====================== | |
(close) @closeButton | |
(OK) @okButton | |
""" | |
self.w = ezui.EZSheet( | |
content=content, | |
size=(250, "auto"), | |
parent=parent, | |
controller=self, | |
defaultButton="okButton" | |
) | |
glyphName = getExtensionDefault(defaults_key, fallback='') | |
self.w.setItemValue("glyphName", glyphName) | |
self.w.getItem("closeButton").bind(chr(27), []) | |
def started(self): | |
self.w.open() | |
def okButtonCallback(self, sender): | |
data = self.w.content.getItemValues() | |
glyphName = data["glyphName"] | |
if glyphName: | |
for font in self.fonts: | |
if glyphName not in font: | |
font.newGlyph(glyphName) | |
OpenGlyphWindow(font[glyphName]) | |
setExtensionDefault(defaults_key, glyphName) | |
self.closeSheet() | |
def closeButtonCallback(self, sender): | |
self.closeSheet() | |
def closeSheet(self): | |
self.fonts = None | |
self.w.close() | |
class HeadlessFontController(Subscriber, ezui.WindowController): | |
debug = True | |
supportedFontFormats = ["ufo", "ufoz", "otf", "ttf"] | |
changedCharacter = "•" | |
def build(self): | |
content = """ | |
|-------| @fonts | |
| | | |
|-------| | |
> (+-) @import | |
> (New) @newFont | |
> (Save) @save | |
""" | |
descriptionData = dict( | |
fonts=dict( | |
insetFooter=True, | |
columnDescriptions=[ | |
dict( | |
identifier="changed", | |
title="", | |
width=10 | |
), | |
dict( | |
identifier="font", | |
title="UFO file" | |
), | |
], | |
dropSettings=dict( | |
pasteboardTypes=["fileURL"], | |
dropCandidateCallback=self.fontsDropCandidateCallback, | |
performDropCallback=self.fontsDropPerformCallback | |
) | |
) | |
) | |
toolbar = dict( | |
autosaveName="headLessFontToolbar", | |
allowCustomization=True, | |
displayMode="image", | |
contents=[ | |
dict( | |
identifier="toolbarFontOverview", | |
image=ezui.makeImage(imageName="prefToolbarFontOverview"), | |
text="Font Overview" | |
), | |
dict( | |
identifier="toolbarGlyphEditor", | |
image=ezui.makeImage(imageName="prefToolbarGlyphView"), | |
text="Glyph Editor" | |
), | |
dict( | |
identifier="toolbarSpaceCenter", | |
image=ezui.makeImage(imageName="toolbarSpaceCenterAlternate"), | |
text="Space Center" | |
), | |
dict( | |
identifier="toolbarFontInfo", | |
image=ezui.makeImage(imageName="toolbarFontInfo"), | |
text="Font Info" | |
), | |
] | |
) | |
self.w = ezui.EZWindow( | |
autosaveName="HeadlessFontController", | |
size="auto", | |
minSize=(260, 250), | |
content=content, | |
descriptionData=descriptionData, | |
toolbar=toolbar, | |
margins=0, | |
controller=self | |
) | |
self.w.getNSWindow().setToolbarStyle_(AppKit.NSWindowToolbarStyleUnified) | |
self.w.getNSWindow().setTitleVisibility_(True) | |
self.windowControllers = [] | |
window = self.getWindow() | |
window.bind("should close", self.shouldCloseCallback) | |
def started(self): | |
self.w.open() | |
def destroy(self): | |
table = self.w.getItem("fonts") | |
items = table.get() | |
for item in items: | |
self.removeFontItem(item) | |
def getSelectedFonts(self): | |
return [item["fontObject"] for item in self.getSelectedItems()] | |
def getSelectedItems(self): | |
table = self.w.getItem("fonts") | |
items = table.get() | |
return [items[index] for index in table.getSelectedIndexes()] | |
def createFontItem(self, path=None): | |
windowController = AppKit.NSWindowController.alloc().init() | |
self.windowControllers.append(windowController) | |
font = RFont(pathOrObject=path, showInterface=False) | |
font.shallowDocument().addWindowController_(windowController) | |
if path: | |
documentController = AppKit.NSDocumentController.sharedDocumentController() | |
documentController.noteNewRecentDocumentURL_(AppKit.NSURL.fileURLWithPath_(path)) | |
table = self.w.getItem("fonts") | |
item = table.makeItem( | |
font=os.path.basename(path), | |
fontObject=font, | |
changed="" | |
) | |
self.addAdjunctObjectToObserve(font) | |
return item | |
def removeFontItem(self, item, closeEveryThing=True): | |
font = item["fontObject"] | |
self.removeObservedAdjunctObject(font) | |
document = font.shallowDocument() | |
windowControllers = document.windowControllers() | |
if not closeEveryThing: | |
windowControllers = [windowController for windowController in windowControllers if windowController in self.windowControllers] | |
for windowController in windowControllers: | |
windowController.close() | |
document.removeWindowController_(windowController) | |
if windowController in self.windowControllers: | |
self.windowControllers.remove(windowController) | |
if closeEveryThing: | |
document.close() | |
# subscriber notifications | |
def fontDocumentDidSave(self, info): | |
font = info["font"] | |
table = self.w.getItem("fonts") | |
items = table.get() | |
indexes = [] | |
for index, item in enumerate(items): | |
if item["fontObject"].path == font.path: | |
item["changed"] = "" | |
indexes.append(index) | |
if indexes: | |
table.reloadData(indexes) | |
def adjunctFontDidChange(self, info): | |
font = info["font"] | |
table = self.w.getItem("fonts") | |
items = table.get() | |
indexes = [] | |
for index, item in enumerate(items): | |
if item["fontObject"].path == font.path: | |
item["changed"] = self.changedCharacter | |
indexes.append(index) | |
if indexes: | |
table.reloadData(indexes) | |
# window notifications | |
def shouldCloseCallback(self, sender): | |
windowController = self.w.getNSWindowController() | |
table = self.w.getItem("fonts") | |
items = table.get() | |
unsavedFonts = [item["fontObject"] for item in items if item["fontObject"].hasChanged()] | |
if unsavedFonts: | |
def askYesNoCallback(result): | |
for font in unsavedFonts: | |
if result: | |
font.save() | |
else: | |
font.asDefcon().dirty = False | |
self.w.close() | |
paths = [os.path.basename(font.path) for font in unsavedFonts] | |
self.showAskYesNo( | |
f"Do you want to save the changes made to the documents '{', '.join(paths )}'", | |
informativeText="Your changes will be lost if you don’t save them.", | |
callback=askYesNoCallback | |
) | |
return len(unsavedFonts) == 0 | |
# ezui notifications | |
def toolbarSpaceCenterCallback(self, sender): | |
for font in self.getSelectedFonts(): | |
OpenSpaceCenter(font) | |
def toolbarFontInfoCallback(self, sender): | |
fonts = self.getSelectedFonts() | |
if fonts: | |
OpenFontInfoSheet(fonts[0]) | |
def toolbarFontOverviewCallback(self, sender): | |
table = self.w.getItem("fonts") | |
items = table.get() | |
for item in self.getSelectedItems(): | |
self.removeFontItem(item, ) | |
items.remove(item) | |
item["fontObject"].openInterface() | |
table.set(items) | |
def toolbarGlyphEditorCallback(self, sender): | |
self.fontsDoubleClickCallback(sender) | |
def fontsDoubleClickCallback(self, sender): | |
fonts = self.getSelectedFonts() | |
if fonts: | |
OpenGlyphController(self.w, fonts) | |
def fontsDropCandidateCallback(self, info): | |
sender = info["sender"] | |
droppedItems = info["items"] | |
existingItems = sender.get() | |
existingPaths = [item["fontObject"].path for item in existingItems] | |
droppedURLS = sender.getDropItemValues(droppedItems, "fileURL") | |
paths = [url.path() for url in droppedURLS if url.pathExtension() in self.supportedFontFormats and url.path() not in existingPaths] | |
return bool(paths) | |
def fontsDropPerformCallback(self, info): | |
sender = info["sender"] | |
droppedItems = info["items"] | |
existingItems = sender.get() | |
existingPaths = [item["fontObject"].path for item in existingItems] | |
droppedURLS = sender.getDropItemValues(droppedItems, "fileURL") | |
paths = [url.path() for url in droppedURLS if url.pathExtension() in self.supportedFontFormats and url.path() not in existingPaths] | |
table = self.w.getItem("fonts") | |
for path in paths: | |
item = self.createFontItem(path) | |
existingItems.append(item) | |
table.set(existingItems) | |
return bool(paths) | |
def newFontCallback(self, sender): | |
def putFileCallback(result): | |
font = RFont(showInterface=False) | |
font.save(result) | |
font.close() | |
table = self.w.getItem("fonts") | |
items = table.get() | |
item = self.createFontItem(result) | |
items.append(item) | |
table.set(items) | |
self.showPutFile(callback=putFileCallback, fileTypes=["ufo"], messageText="Save the font on disk") | |
def saveCallback(self, sender): | |
for item in self.getSelectedItems(): | |
item["fontObject"].shallowDocument().saveDocument_(None) | |
item["changed"] = "" | |
table = self.w.getItem("fonts") | |
table.reloadData(table.getSelectedIndexes()) | |
def importAddCallback(self, sender): | |
def getFileCallback(result): | |
windowController = self.w.getNSWindowController() | |
table = self.w.getItem("fonts") | |
items = table.get() | |
for path in result: | |
item = self.createFontItem(path) | |
items.append(item) | |
table.set(items) | |
self.showGetFile( | |
messageText="Selecte a font.", | |
fileTypes=self.supportedFontFormats, | |
allowsMultipleSelection=True, | |
callback=getFileCallback | |
) | |
def importRemoveCallback(self, sender): | |
windowController = self.w.getNSWindowController() | |
table = self.w.getItem("fonts") | |
selection = table.getSelectedIndexes() | |
items = table.get() | |
for index in reversed(sorted(selection)): | |
item = items[index] | |
del items[index] | |
self.removeFontItem(item) | |
font = item["fontObject"] | |
if font.hasChanged(): | |
fontToSave = font.asDefcon() | |
def askYesNoCallback(result): | |
if result: | |
fontToSave.save() | |
result = self.showAskYesNo( | |
f"Do you want to save the changes made to the document '{os.path.basename(font.path)}'?", | |
informativeText="Your changes will be lost if you don’t save them.", | |
callback=askYesNoCallback | |
) | |
font.close() | |
table.set(items) | |
OpenWindow(HeadlessFontController) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment