2.5.3 Neue Definitionen von Beschriftungsbefehlen
Dieser Abschnitt behandelt die Definition von neuen Textbeschriftungsbefehlen.
Syntax der Definition von Textbeschriftungsbefehlen | ||
Über Eigenschaften | ||
Ein vollständiges Bespiel | ||
Eingebaute Befehle anpassen |
Syntax der Definition von Textbeschriftungsbefehlen
Neue Textbeschriftungsbefehle können mit dem
define-markup-command
-Scheme-Makro definiert werden.
(define-markup-command (befehl-bezeichnung layout props Arg1 Arg2 ...) (Arg1-typ? Arg2-typ? ...) [ #:properties ((Eigenschaft1 Standard-Wert1) ...) ] ..Befehlkörper..) |
Die Argumente sind:
- befehl-bezeichnung
die Bezeichnung des Befehls
- layout
die ‚layout‘-Definition
- props
eine Liste an assoziativen Listen, in der alle aktiven Eigenschaften enthalten sind
- argi
das ite Befehlsargument
- argi-type?
eine Eigenschaft für das ite Argument
Wenn der Befehl Eigenschaften des props
-Arguments benutzt,
kann das #:properties
-Schlüsselwort benutzt werden um zu
bestimmen, welche Eigenschaften mit welchen Standard-Werten benutzt
werden.
Argumente werden nach ihrem Typ unterschieden:
- eine Textbeschriftung entspricht einem Typenprädikat
markup?
; - eine Textbeschriftungsliste entspricht einem Typenprädikat
markup-list?
; - jedes andere Scheme-Objekt entspricht Typenprädikaten wie etwa
list?
,number?
,boolean?
, usw.
Es gibt keine Einschränkung in der Reihenfolge der Argumente (nach
den Standard-Argumenten layout
und props
). Textbeschriftungsfunktionen,
die als letztes Argument eine Textbeschriftung haben, haben die
Besonderheit, dass sie auf Textbeschriftungslisten angewendet werden
können, und das Resultat ist eine Textbeschriftungsliste, in der
die Textbeschriftungsfuktion (mit den angegebenen Argumenten am Anfang)
auf jedes Element der originalen Textbeschriftungsliste angewendet
wurde.
Da das Wiederholen der Argumente am Anfang bei der Anwendung einer Textbeschriftungsfunktion auf eine Textbeschriftungsliste for allem für Scheme-Argumente sparsam ist, kann man Leistungseinbußen vermeiden, indem man nur Scheme-Argumente für die Argumente am Anfang einsetzt, wenn es sich um Textbeschriftungsfunktionen handelt, die eine Textbeschriftung als letztes Argument haben.
Über Eigenschaften
Die layout
- und props
-Argumente der Textbeschriftungsbefehle
bringen einen Kontext für die Interpretation der Beschriftung:
Schriftgröße, Zeilenlänge usw.
Das layout
-Argument greift auf Eigenschaften zu, die in der
paper
-Umgebung definiert werden, indem man die ly:output-def-lookup
-Funktion
benutzt. Beispielsweise liest man die Zeilenlänge (die gleiche, die auch in
Partituren benutzt wird) aus mit:
(ly:output-def-lookup layout 'line-width)
Das props
-Argument stellt einige Eigenschaften für die Textbeschriftungsbefehle
zur Verfügung. Beispielsweise wenn der Überschrifttext einer
book
-Umgebung interpretiert wird, werden alle Variablen, die
in der \header
-Umgebung definiert werden, automatisch zu props
hinzugefügt, sodass die Beschriftung auf Titel, Komponist usw. der
book
-Umgebung zugreifen kann. Das ist auch eine Möglichkeit, das
Verhalten eines Beschriftungsbefehls zu konfigurieren: Wenn etwa ein
Befehl die Schriftgröße während der Verarbeitung einsetzt, wird die
Schriftgröße aus den props
ausgelesen und nicht mit einem eigenen
font-size
-Argument definiert. Beim Aufruf des Beschriftungsbefehls
kann der Wert der Schriftgröße geändert werden, womit sich auch das Verhalten
des Befehls verändert. Benutzen Sie das #:properties
-Schlüsselwort
von define-markup-command
um zu definieren, welche Eigenschaften aus den
props
-Argumenten ausgelesen werden sollen.
Das Beispiel im nächsten Abschnitt illustriert, wie man auf Eigenschaften in einem Beschriftungsbefehl zugreifen und sie verändern kann.
Ein vollständiges Bespiel
Das folgende Beispiel definiert einen Beschriftungsbefehl, der einen doppelten Kasten um einen Text zeichnet.
Zuerst wollen wir ein annäherndes Ergebnis mit Textbeschriftungen definieren.
Nach Stöbern in
Textbeschriftungsbefehle finden wir den Befehl
\box
:
\markup \box \box HELLO
Wir wollen aber etwas mehr Abstand (engl. padding) zwischen dem Text und dem Kasten.
Nach der Dokumentation von \box
hat der Befehl eine
box-padding
-Eigenschaft, die den Standardwert von 0.2 hat. Die
Dokumentation zeit auch, wir man den Wert verändert:
\markup \box \override #'(box-padding . 0.6) \box A
Auch der Abstand zwischen den zwei Kästen ist uns zu klein und soll auch vergrößert werden:
\markup \override #'(box-padding . 0.4) \box \override #'(box-padding . 0.6) \box A
Diese lange Textbeschriftung immer wieder schreiben zu müssen, ist
anstrengend. Hier kömmt ein Textbeschriftungsbefehl ins Spiel. Wir
schreiben uns alle einen double-box
-Beschriftungsbefehl, der
ein Argument annimmt (den Text). Er zeichnet zwei Kästen mit genügend Abstand:
#(define-markup-command (double-box layout props text) (markup?) "Draw a double box around text." (interpret-markup layout props #{\markup \override #'(box-padding . 0.4) \box \override #'(box-padding . 0.6) \box { #text }#})) |
oder äquivalent
#(define-markup-command (double-box layout props text) (markup?) "Draw a double box around text." (interpret-markup layout props (markup #:override '(box-padding . 0.4) #:box #:override '(box-padding . 0.6) #:box text))) |
text
ist die Bezeichnung des Arguments dieses Befehls,
und markup?
ist seine Art: hiermit wird der Befehl als
Beschriftungsbefehl identifiziert. Die interpret-markup
-Funktion
wird in den meisten Beschriftungsbefehlen benutzt: sie erstellt einen
Stencil, wobei layout
, props
und eine Beschriftung benutzt
werden. Im zweiten Fall wird diese Beschriftung durch das
markup
-Scheme-Makro erstellt, siehe auche
Beschriftungskonstruktionen in Scheme.
Die Transformation des \markup
-Ausdrucks in einen
Scheme-Beschriftungsausdruck geschieht durch Umschreiben des LilyPond-Codes
in Scheme-Code.
Der neue Befehl kann wie folgt benutzt werden:
\markup \double-box A
Es wäre schön, den double-box
-Befehl noch konfigurierbar zu gestalten:
in unserem Fall sind die Werte von box-padding
direkt definiert und
können nicht mehr vom Benutzer verändert werden. Es wäre auch besser, wenn
der Abstand zwischen den beiden Kästen vom Abstand zwischen dem inneren Kasten
und dem Text unterschieden werden könnte. Eine neue Eigenschaft muss also
definiert werden: inter-box-padding
für den Abstand zwischen den Kästen.
box-padding
wird für den inneren Abstand benutzt. Der neue Befehl wird
so definiert:
#(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Draw a double box around text." (interpret-markup layout props #{\markup \override #`(box-padding . ,inter-box-padding) \box \override #`(box-padding . ,box-padding) \box { #text } #})) |
Wiederum wäre die entsprechende Version mit dem markup
-Makro
so aussehen:
#(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Draw a double box around text." (interpret-markup layout props (markup #:override `(box-padding . ,inter-box-padding) #:box #:override `(box-padding . ,box-padding) #:box text))) |
In diesem Code wird das #:properties
-Schlüsselwort benutzt, sodass
die Eigenschaften inter-box-padding
und box-padding
aus dem
props
-Argument ausgelesen werden, und Standardwerte werden gegeben,
falls die Eigenschaften nicht definiert sein sollten.
Dann werden diese Werte benutzt, um die box-padding
-Eigenschaft
zu verändert, die von beiden \box
-Befehlen benutzt wird. Beachten
Sie Akzent und das Komma des \override
-Arguments: hiermit kann man
einen Variablenwert in einen wörtlichen Ausdruck überführen.
Jetzt kann der Befehl in Beschriftungen benutzt werden und der Abstand der Kästen kann angepasst werden:
#(define-markup-command (double-box layout props text) (markup?) #:properties ((inter-box-padding 0.4) (box-padding 0.6)) "Draw a double box around text." (interpret-markup layout props #{\markup \override #`(box-padding . ,inter-box-padding) \box \override #`(box-padding . ,box-padding) \box { #text } #})) \markup \double-box A \markup \override #'(inter-box-padding . 0.8) \double-box A \markup \override #'(box-padding . 1.0) \double-box A
Eingebaute Befehle anpassen
Ein guter Weg, einen neuen Beschriftungsbefehl zu schreiben, ist es, als Vorbild einen existierenden zu nehmen. Die meisten Beschriftungsbefehle, die LilyPond mitbringt, finden sich in der Datei ‘scm/define-markup-commands.scm’.
Man könnte beispielsweise den Befehl \draw-line
, der eine Linie
zeichnet, anpassen, sodass er eine Doppellinie zeichnet. Der
Befehl \draw-line
ist wie folgend definiert (Dokumentation entfernt):
(define-markup-command (draw-line layout props dest) (number-pair?) #:category graphic #:properties ((thickness 1)) "..documentation.." (let ((th (* (ly:output-def-lookup layout 'line-thickness) thickness)) (x (car dest)) (y (cdr dest))) (make-line-stencil th 0 0 x y))) |
Um einen neuen Befehl, der auf einem existierenden basiert, zu definieren,
wird die Befehlsdefinition kopiert und die Bezeichnung des Befehls
geändert. Das #:category
-Schlagwort kann entfernt werden,
weil es nur zur Erstellung der LilyPond-Dokumentation eingesetzt wird
und keine Bedeutung für selbstdefinierte Befehle hat.
(define-markup-command (draw-double-line layout props dest) (number-pair?) #:properties ((thickness 1)) "..documentation.." (let ((th (* (ly:output-def-lookup layout 'line-thickness) thickness)) (x (car dest)) (y (cdr dest))) (make-line-stencil th 0 0 x y))) |
Dann braucht man eine Eigenschaft, um den Abstand zwischen den zwei
Linien zu definieren, als line-gap
bezeichnet und etwa mit
dem Standardwert 0.6:
(define-markup-command (draw-double-line layout props dest) (number-pair?) #:properties ((thickness 1) (line-gap 0.6)) "..documentation.." ... |
Schließlich wird der Code, der die zwei Linien zeichnet, hinzugefügt.
Zwei Aufrufe an make-line-stencil
werden benutzt, um beide Linien
zu zeichnen, und die beiden sich daraus ergebenden Stencils werden mit
ly:stencil-add
kombiniert:
#(define-markup-command (my-draw-line layout props dest) (number-pair?) #:properties ((thickness 1) (line-gap 0.6)) "..documentation.." (let* ((th (* (ly:output-def-lookup layout 'line-thickness) thickness)) (dx (car dest)) (dy (cdr dest)) (w (/ line-gap 2.0)) (x (cond ((= dx 0) w) ((= dy 0) 0) (else (/ w (sqrt (+ 1 (* (/ dx dy) (/ dx dy)))))))) (y (* (if (< (* dx dy) 0) 1 -1) (cond ((= dy 0) w) ((= dx 0) 0) (else (/ w (sqrt (+ 1 (* (/ dy dx) (/ dy dx)))))))))) (ly:stencil-add (make-line-stencil th x y (+ dx x) (+ dy y)) (make-line-stencil th (- x) (- y) (- dx x) (- dy y))))) \markup \my-draw-line #'(4 . 3) \markup \override #'(line-gap . 1.2) \my-draw-line #'(4 . 3)