UI examples

Add a custom entry in RoboFont’s main menu

from AppKit import *
from vanilla.vanillaBase import VanillaCallbackWrapper

class BuildMenu(object):
    
    def __init__(self):
        
        myMenuTitle = "TypeMedia Awsome scripts"
        
        # create a new menu
        myMenu = NSMenu.alloc().initWithTitle_(myMenuTitle)
        # create a new item
        item = myMenu.addItemWithTitle_action_keyEquivalent_("do it", "action:", "m")
        # create a callback
        self.callbackwrapper = VanillaCallbackWrapper(self.doIt)
        # set the callback
        item.setTarget_(self.callbackwrapper)
        # setting modifiermaks
        item.setKeyEquivalentModifierMask_(NSControlKeyMask | NSCommandKeyMask)
        
        
        # ask the main menu
        menu = NSApp().mainMenu()
        # add item
        newItem = menu.addItemWithTitle_action_keyEquivalent_(myMenuTitle, "", "")
        # set submenu
        newItem.setSubmenu_(myMenu)
    
    
    def doIt(self, sender):
        print "hello"
        
BuildMenu()

Glyph preview

from vanilla import Window
from mojo.glyphPreview import GlyphPreview
from mojo.events import addObserver, removeObserver
from mojo.roboFont import OpenWindow

class Preview:

    def __init__(self):
        ## create a window
        self.w = Window((400, 400), "Preview", minSize=(100, 100))

        ## add a GlyphPreview to the window
        self.w.preview = GlyphPreview((0, 0, -0, -0))

        ## set the currentGlyph
        self.setGlyph(CurrentGlyph())

        ## add an observer when the glyph changed in the glyph view
        addObserver(self, "_currentGlyphChanged", "currentGlyphChanged")

        ## bind a windows close callback to this object
        self.w.bind("close", self.windowClose)
        ## open the window
        self.w.open()

    def _currentGlyphChanged(self, info):
        ## notification callback when the glyph changed in the glyph view
        ## just set the new glyph in the glyph preview
        self.setGlyph(CurrentGlyph())

    def setGlyph(self, glyph):
        ## setting the glyph in the glyph Preview
        self.w.preview.setGlyph(glyph)

    def windowClose(self, sender):
        ## remove the observer if the window closes
        removeObserver(self, "_currentGlyphChanged")


## open the window with OpenWindow, so it can not be open twice
OpenWindow(Preview)

Simple font window

A simpler (faster) font window without glyph previews, just a list of glyph names.

from vanilla import *
from defconAppKit.windows.baseWindow import BaseWindowController

from mojo.UI import OpenGlyphWindow, OpenSpaceCenter, OpenFontInfoSheet


class SimpleFontWindow(BaseWindowController):
    
    def __init__(self, font):
        
        self._font = font
        
        self._canUpdateChangeCount = True
        
        self.w = Window((250, 500), "SimpleFontWindow", minSize=(200, 300))
        
        glyphs = font.keys()
        glyphs.sort()
        
        self.w.glyphs = List((0, 0, -0, -0), glyphs, doubleClickCallback=self.openGlyph)
        
        toolbarItems = [
                dict(itemIdentifier="spaceCenter",
                     label="Space Center",
                     imageNamed="toolbarSpaceCenterAlternate",
                     callback=self.openSpaceCenter
                     ),
                dict(itemIdentifier="fontInfo",
                     label="Font Info",
                     imageNamed="toolbarFontInfo",
                     callback=self.openFontInfo
                     )
                ]
        self.w.addToolbar(toolbarIdentifier="SimpleToolbar", toolbarItems=toolbarItems)
        
        windowController = self.w.getNSWindowController()
        windowController.setShouldCloseDocument_(True)
        self._font.UIdocument().addWindowController_(windowController)
                
        self._font.addObserver(self, "fontChanged", "Font.Changed")

        self.setUpBaseWindowBehavior()
        self.w.open()
    
    def openGlyph(self, sender):
        sel = sender.getSelection()
        if sel:
            i = sel[0]        
            name = sender[i]
            self._canUpdateChangeCount = False
            OpenGlyphWindow(self._font[name])
            self._canUpdateChangeCount = True
                
    def openSpaceCenter(self, sender):
        self._canUpdateChangeCount = False
        OpenSpaceCenter(self._font)
        self._canUpdateChangeCount = True
    
    def openFontInfo(self, sender):
        self._canUpdateChangeCount = False
        OpenFontInfoSheet(self._font, self.w)
        self._canUpdateChangeCount = True
    
    # notifications
    def fontChanged(self, notification):
        if self._canUpdateChangeCount:
            self._font.UIdocument().updateChangeCount_(0)
    
    
fonts = OpenFont(showUI=False)
if not isinstance(fonts, list):
    fonts = [fonts]

for font in fonts:
    ## small bug if the font has no units per em set (already fixed in the dev version)
    if font.info.unitsPerEm is None:
        font.info.unitsPerEm = 1000

    SimpleFontWindow(font)

Simple help window

from mojo.UI import HelpWindow

HelpWindow("http://robofont.com")

Custom accordion window

from mojo.UI import AccordionView
from vanilla import *

class MyInspector:

    def __init__(self):

        self.w = FloatingWindow((200, 600))

        self.firstItem = TextEditor((10, 10, -10, -10))

        self.secondItem = List((0, 0, -0, -0), ["a", "b", "c"])

        self.thirdItem = Tabs((10, 10, -10, -10), ["1", "2", "3"])

        self.fourthItem = Group((0, 0, -0, -0))

        self.fourthItem.checkBox = CheckBox((10, 10, 100, 22), "CheckBox")
        self.fourthItem.editText = EditText((10, 40, -10, 22))


        descriptions = [
                       dict(label="first item", view=self.firstItem, size=200, collapsed=False, canResize=False),
                       dict(label="second item", view=self.secondItem, minSize=100, size=140, collapsed=True, canResize=True),
                       dict(label="third item", view=self.thirdItem, minSize=100, size=140, collapsed=True, canResize=False),
                       dict(label="fourth item", view=self.fourthItem, size=140, collapsed=False, canResize=False)
                       ]

        self.w.accordionView = AccordionView((0, 0, -0, -0), descriptions)

        self.w.open()

MyInspector()

Change position of values in Space Center

from mojo.events import addObserver

class MetricsOnTop(object):
    
    def __init__(self):
        # add an obserer when a space center did open
        addObserver(self, "metricsOnTop", "spaceCenterDidOpen")
    
    def metricsOnTop(self, notification):
        # get the window
        window = notification["window"]
        # get the space center object
        spaceCenter = window.getSpaceCenter()
        
        # vanilla rocks!
        # get the position of each element
        l, t, r, b = spaceCenter.glyphLineView.getPosSize()
        ll, tt, rr, bb = spaceCenter.inputScrollView.getPosSize()

        tt = abs(tt)
        # and use the old positions to reorganise the views
        spaceCenter.glyphLineView.setPosSize((l, t+tt+1, r, -0))
        spaceCenter.inputScrollView.setPosSize((ll, t, rr, bb))
        spaceCenter.hl.setPosSize((l, t+tt, 0, 1))
    
    
MetricsOnTop()

Open a new window for current glyph

from mojo.UI import OpenGlyphWindow

g = CurrentGlyph()
if g is not None:
    OpenGlyphWindow(g, newWindow=True)

Custom Multi-Line View

from mojo.UI import MultiLineView
from vanilla import *

class SampleMultiLineView:

    def __init__(self, font):

        self.w = Window((600, 400), minSize=(300, 300))

        self.w.lineView = MultiLineView((0, 0, -0, -0),
                            pointSize=30,
                            selectionCallback=self.lineViewSelectionCallback)
        self.w.lineView.setFont(font)

        glyphs = []
        for glyphName in font.glyphOrder:
            glyphs.append(font[glyphName])

        self.w.lineView.set(glyphs)

        self.w.open()

    def lineViewSelectionCallback(self, sender):
        print sender.getSelectedGlyph()

SampleMultiLineView(CurrentFont())

Change the order of editing tools

from AppKit import NSDragOperationMove
from vanilla import *

from mojo.events import setToolOrder, getToolOrder

toolOrderDragType = "toolOrderDragType"

class ToolOrder:
    
    def __init__(self):
        
        self.w = Window((200, 300), "Tool Orderer")
        
        self.w.tools = List((10, 10, -10, -40), getToolOrder(),
                            dragSettings=dict(type=toolOrderDragType, callback=self.dragCallback),
                            selfDropSettings=dict(type=toolOrderDragType, operation=NSDragOperationMove, callback=self.dropListSelfCallback),
                            )
        
        self.w.apply = Button((10, -30, -10, 22), "Apply", callback=self.applyCallback)
        self.w.open()
    
    def applyCallback(self, sender):
        setToolOrder(self.w.tools.get())
        
    def dragCallback(self, sender, indexes):
        return indexes
        
    def dropListSelfCallback(self, sender, dropInfo):
        isProposal = dropInfo["isProposal"]
        
        if not isProposal:
            indexes = [int(i) for i in sorted(dropInfo["data"])]
            indexes.sort()
            source = dropInfo["source"]
            rowIndex = dropInfo["rowIndex"]

            items = sender.get()

            toMove = [items[index] for index in indexes]

            for index in reversed(indexes):
                del items[index]

            rowIndex -= len([index for index in indexes if index < rowIndex])
            for font in toMove:
                items.insert(rowIndex, font)
                rowIndex += 1

            sender.set(items)
        return True
        
        
ToolOrder()

Draw anti-aliased curve segments

from AppKit import *
from fontTools.pens.basePen import BasePen

from mojo.events import addObserver
from lib.tools.defaults import getDefaultColor


class AntialiasCocoaPen(BasePen):
    
    """
    - create a nsBezierPath for each segment.
    - Straight lines are ignored.
    """
    
    def __init__(self, glyphSet):
        BasePen.__init__(self, glyphSet)
        self.path = NSBezierPath.bezierPath()
        self.prevPoint = None
        self.firstPoint = None
        
    def _moveTo(self, pt):
        self.firstPoint = pt
        self.prevPoint = pt
    
    def _lineTo(self, pt):
        if pt[0] != self.prevPoint[0] and pt[1] != self.prevPoint[1]:
            # only draw the antialiased line if x or y is different
            self.path.moveToPoint_(self.prevPoint)
            self.path.lineToPoint_(pt)
        self.prevPoint = pt
    
    def _curveToOne(self, pt1, pt2, pt3):
        self.path.moveToPoint_(self.prevPoint)
        self.path.curveToPoint_controlPoint1_controlPoint2_(pt3, pt1, pt2)
        self.prevPoint = pt3
    
    def closePath(self):
        if self.firstPoint != self.prevPoint:
            self._lineTo(self.firstPoint)
        self.prevPoint = None


class NiceLines(object):
    
    def __init__(self):
        ## add observer when the glyph view draws the content
        addObserver(self, "myDraw", "drawBackground")
        ## get the stroke color from the defaults
        self.strokeColor = getDefaultColor("glyphViewStrokeColor")
        
    def myDraw(self,info):
        glyph = info["glyph"]
        ## initiate the pen
        pen = AntialiasCocoaPen(glyph.getParent())
        ## draw the glyph in the pen
        glyph.draw(pen)
        ## set the stroke color
        self.strokeColor.set()
        ## set the line width of the path, the same as the scale of the glyph view
        pen.path.setLineWidth_(info["scale"])
        ## stroke the path
        pen.path.stroke()
        
## install the observer
NiceLines()

Object browser

import vanilla
import mojo

"""
Example usage of the vanilla objectBrowser.
"""


class ObjectBrowserController():

        def __init__(self, inspectObject):

            if inspectObject is None:
                raise TypeError, "can not inspect None value"

            self.w = vanilla.Window((400, 400),
                                   "inspect %s" %inspectObject,
                                   minSize=(100, 100))
            self.w.b = vanilla.ObjectBrowser((0, 0, -0, -0),
                                   inspectObject)
            self.w.open()


#obj = vanilla
#obj = mojo
obj = CurrentFont()

ObjectBrowserController(obj)

Move glyph with sliders