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, installTool

RED = 1, 0, 0, 0.5
BLUE = 0, 0, 1, 0.5

class MyTool(BaseEventTool):

    def setup(self):
        container = self.extensionContainer(identifier="com.roboFont.MyTool.foreground",
                                            location="foreground",
                                            clear=True)

        position = 0, 0
        size = 100, 100
        self.rectLayer = container.appendRectangleSublayer(
            position= position,
            size=     size,
            fillColor=RED,
            visible=False
        )
        self.ovalLayer = container.appendOvalSublayer(
            position= position,
            size=     size,
            fillColor=RED
        )

    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)
        w, h = self.rectLayer.getSize()
        w *= 4
        h *= 4
        self.rectLayer.setSize((w, h))
        self.ovalLayer.setSize((w, h))

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

    def mouseDragged(self, point, delta):
        print("mouse dragged")
        self.rectLayer.setPosition((point.x, point.y))
        self.ovalLayer.setPosition((point.x, point.y))

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

    def mouseUp(self, point):
        print("mouse up")
        w, h = self.rectLayer.getSize()
        w *= 0.25
        h *= 0.25
        self.rectLayer.setSize((w, h))
        self.ovalLayer.setSize((w, h))

    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.rectLayer.setPosition((point.x, point.y))
        self.ovalLayer.setPosition((point.x, point.y))

    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.rectLayer.setVisible(True)
            self.ovalLayer.setVisible(False)
        # > otherwise, shape is an oval
        else:
            self.rectLayer.setVisible(False)
            self.ovalLayer.setVisible(True)

        # change color based on 'option' key:
        # > if 'option' is pressed, color is blue
        if modifiers['optionDown']:
            self.rectLayer.setFillColor(BLUE)
            self.ovalLayer.setFillColor(BLUE)
        # > otherwise, color is red
        else:
            self.rectLayer.setFillColor(RED)
            self.ovalLayer.setFillColor(RED)

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

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

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


if __name__ == '__main__':
    myTool = MyTool()
    installTool(myTool)

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 subscriber import WindowController
from mojo.events import installTool, uninstallTool, getToolOrder

class MyToolActivator(WindowController):

    def __init__(self, tool):
        super().__init__()
        self.tool = tool

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

    def started(self):
        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 destroy(self):
        # if the tool is active, remove it
        tools = getToolOrder()
        if self.tool.__class__.__name__ in tools:
            uninstallTool(self.tool)


MyToolActivator(MyTool())
Last edited on 15/07/2021