Adding properties and types

This appendix describes methods to define new context and grob properties as well as new music and grob types.

The reader should be warned that these are not proper interfaces at the moment. They may stop working in future versions when more extensibility is added.

This is adapted from the famous regression test scheme-text-spanner.ly by David Nalesnik.

For context types, there is proper extensibility as detailed at Defining new context types.

New properties

Defining a property comes down to defining a predicate used to check values given to this property by the user. This is done through setting object properties on the property name symbol. The relevant properties are translation-type?, for context properties, and backend-type?, for grob properties.

#(set-object-property! 'myAwesomeContextProperty 'translation-type? number?)
#(set-object-property! 'my-awesome-grob-property 'backend-type? boolean?)

Music properties are not type-checked, thus no definition is required.

New event class

The existing function define-event-class takes a class name and a parent class name. If no particular event class stands out for the parent, music-event is often a fair choice.

#(define-event-class 'highlight-event 'span-event)

New music type

Use the following function:

#(define (define-event! type properties)
   (set-object-property! type
                         'music-description
                         (cdr (assq 'description properties)))
   (set! properties (assoc-set! properties 'name type))
   (set! properties (assq-remove! properties 'description))
   (hashq-set! music-name-to-property-table type properties)
   (set! music-descriptions
         (sort (cons (cons type properties)
                     music-descriptions)
               alist<?)))

The type argument is the name of the new music type as a symbol, and properties is an associative list of properties and defaults. The most important properties are description and types. The description is mandatory. The types are optional music classes this event type is made to belong to.

Here is how one might define an event used for drawing highlight boxes:

#(define-event!
   'HighlightEvent
   '((description . "Used to signal where colored highlights start and stop.")
     (types . (post-event highlight-event event))))

New grob type

Use this function:

#(define (define-grob! grob-name grob-entry)
   (set! all-grob-descriptions
         (cons ((@@ (lily) completize-grob-entry)
                (cons grob-name grob-entry))
               all-grob-descriptions)))

Defining the new grob type is done in two steps. First, call define-grob! with the name of the new grob type and an alist of default properties. The alist must contain a meta entry with the class (Item, Spanner or Paper_column, see Grob flavors and line breaks) and a list of interfaces. The interfaces corresponding to the class (just the item-interface in this case) are added automatically.

#(define-grob!
   'JustText
   `((stencil . ,ly:text-interface::print)
     (X-extent . ,empty-interval)
     (Y-extent . ,empty-interval)
     (meta . ((class . Item)
              (interfaces . (text-interface))))))

After this call, the list of grob descriptions must be reset.

\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
}

Grob interfaces should not need any particular definition.