Skip to content

Instantly share code, notes, and snippets.

@typemytype
Last active May 22, 2023 19:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save typemytype/45293f9b29f29cd2458157a44f8fad94 to your computer and use it in GitHub Desktop.
Save typemytype/45293f9b29f29cd2458157a44f8fad94 to your computer and use it in GitHub Desktop.
# 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