The script below demonstrates how interactive tools work. The tool doesn’t do anything useful – it just shows how to react to mouse and keyboard events, and prints out some information for each event that is triggered.

from mojo.events import BaseEventTool
from mojo.drawingTools import *

class MyTool(BaseEventTool):

    def setup(self):
        self.position = None
        self.size = 20
        self.shape = oval
        self.color = 1, 0, 0, 0.5

    def becomeActive(self):
        print("tool became active")

    def becomeInactive(self):
        print("tool became inactive")

    def mouseDown(self, point, clickCount):
        # getGLyph returns the current glyph as an RGlyph object
        print("mouse down", self.getGlyph(), clickCount)
        self.size *= 4

    def rightMouseDown(self, point, event):
        print("right mouse down")

    def mouseDragged(self, point, delta):
        print("mouse dragged")
        self.position = point

    def rightMouseDragged(self, point, delta):
        print("right mouse dragged")

    def mouseUp(self, point):
        print("mouse up")
        self.size *= 0.25

    def keyDown(self, event):
        # getModifiers returns a dict with all modifiers:
        # Shift, Command, Alt, Option
        print("key down", self.getModifiers())

    def keyUp(self, event):
        print("key up")

    def mouseMoved(self, point):
        self.position = point
        self.refreshView()

    def modifiersChanged(self):
        print("modifiers changed")

        # get modifier keys
        modifiers = self.getModifiers()

        # define shape based on 'shift' key:
        # > if 'shift' is pressed, shape is a rectangle
        if modifiers['shiftDown']:
            self.shape = rect
        # > otherwise, shape is an oval
        else:
            self.shape = oval

        # change color based on 'option' key:
        # > if 'option' is pressed, color is blue
        if modifiers['optionDown']:
            self.color = 0, 0, 1, 0.5
        # > otherwise, color is red
        else:
            self.color = 1, 0, 0, 0.5

        # tell the glyph view to update
        self.refreshView()

    def draw(self, scale):
        # print("drawing...", self.isDragging())

        if self.position is not None:
            # calculate shape position from center
            x = self.position.x - self.size*0.5
            y = self.position.y - self.size*0.5

            # set shape color
            fill(*self.color)
            stroke(None)

            # draw shape
            self.shape(x, y, self.size, self.size)

    def drawBackground(self, scale):
        # print("drawing background...")
        pass

    # def getDefaultCursor(self):
    #     # sets the cursor. (default is an arrow)
    #     return a NSCursor

    # def getToolbarIcon(self):
    #     # sets the toolbar icon. (default is an arrow)
    #     return a NSImage

    def getToolbarTip(self):
        return "My Tool Tip"

    # notifications

    def viewDidChangeGlyph(self):
        print("view changed glyph")

    def preferencesChanged(self):
        print("preferences changed")

Installing a tool

Tools can be installed into the Glyph Editor’s toolbar with installTool:

from mojo.events import installTool
installTool(MyTool())

To make your tool available when RoboFont restarts, create an installation script and save it as a start-up script.

Testing a tool during development

Below is a simple helper to use while working on your own interactive tools. It provides a dialog to install/uninstall a custom tool during development, without having to restart RoboFont.

from vanilla import FloatingWindow, CheckBox
from defconAppKit.windows.baseWindow import BaseWindowController
from mojo.events import installTool, uninstallTool, getToolOrder

class MyToolActivator(BaseWindowController):

    def __init__(self, tool):
        self.w = FloatingWindow((123, 44), 'MyTool')
        self.w.button = CheckBox((10, 10, -10, 24), 'activate', value=True, callback=self.activateToolCallback)
        self.tool = tool
        # install the tool
        installTool(self.tool)
        # initialize the window
        self.setUpBaseWindowBehavior()
        self.w.open()

    def activateToolCallback(self, sender):
        # if the tool is not installed, install it
        if sender.get():
            installTool(self.tool)
        # if the tool is installed, uninstall it
        else:
            uninstallTool(self.tool)

    def windowCloseCallback(self, sender):
        # if the tool is active, remove it
        tools = getToolOrder()
        if self.tool.__class__.__name__ in tools:
            uninstallTool(self.tool)
        super(MyToolActivator, self).windowCloseCallback(sender)

MyToolActivator(MyTool())
Last edited on 21/04/2019