This example shows how to draw something into the glyphs of a Space Center preview.

The script simply draws a rectangle at each glyph’s bounding box. Whenever a glyph is changed, its preview is updated automatically.

Important in this example is the use of representations to create and cache shapes – this approach is recommended for better performance.

from vanilla import FloatingWindow, CheckBox
from defconAppKit.windows.baseWindow import BaseWindowController
from mojo.events import addObserver, removeObserver
from mojo.UI import CurrentSpaceCenter
from defcon import Glyph, registerRepresentationFactory, unregisterRepresentationFactory
import mojo.drawingTools as ctx
from fontTools.pens.cocoaPen import CocoaPen

def BoundsRectFactory(glyph):
    '''A factory function which creates a representation for a given glyph.'''
    pen = CocoaPen(glyph.layer)
    if not glyph.bounds:
        return
    x, y, w, h = glyph.bounds
    pen.moveTo((x, y))
    pen.lineTo((w, y))
    pen.lineTo((w, h))
    pen.lineTo((x, h))
    pen.closePath()
    return pen.path

class BoundsRectViewer(BaseWindowController):

    '''A simple floating window to toggle the preview on/off.'''

    def __init__(self):
        self.w = FloatingWindow((123, 40))
        self.w.preview = CheckBox((10, 10, -10, 20), 'preview', value=True, callback=self.updatePreviewCallback)
        self.setUpBaseWindowBehavior()
        # register the representation factory in the defcon Glyph object
        registerRepresentationFactory(Glyph, "BoundsRectPreview", BoundsRectFactory)
        # add an observer for drawing in the Space Center
        addObserver(self, "drawBoundsRect", "spaceCenterDraw")
        self.w.open()

    def windowCloseCallback(self, sender):
        '''Clear observer & representation factory when window is closed.'''
        super().windowCloseCallback(sender)
        removeObserver(self, "spaceCenterDraw")
        unregisterRepresentationFactory(Glyph, "BoundsRectPreview")

    def updatePreviewCallback(self, sender):
        S = CurrentSpaceCenter()
        if not S:
            return
        S.updateGlyphLineView()

    def drawBoundsRect(self, notification):
        '''Drawing the bounding box around each glyph.'''
        S = CurrentSpaceCenter()
        if not S or not self.w.preview.get():
            return
        # get the current glyph
        glyph = notification['glyph']
        # get representation for glyph
        boundsRect = glyph.getRepresentation("BoundsRectPreview")
        if not boundsRect:
            return
        # draw representation
        scale = notification['scale']
        ctx.save()
        ctx.fill(None)
        ctx.strokeWidth(3 * scale)
        ctx.stroke(0, 1, 0, 0.5)
        ctx.drawPath(boundsRect)
        ctx.restore()

BoundsRectViewer()
Last edited on 15/07/2021