Indenting individual systems: Difference between revisions
mNo edit summary |
mNo edit summary |
||
| Line 7: | Line 7: | ||
Some caveats still apply, though. | Some caveats still apply, though. | ||
* The indenting mechanism is based on an | * The indenting mechanism is based on an arcane misuse of the <code>LeftEdge</code> grob (see {{Issue|3761}}) and may therefore become temporarily or permanently broken by side effects of future development changes in the LilyPond code base. | ||
* The code is not protected against multiple calls per system break. | * The code is not protected against multiple calls per system break. | ||
Latest revision as of 07:49, 6 January 2026
LilyPond scores are indented similar to text paragraphs: all systems except the first one are indented by the same amount (the value of the short-indent property). This layout constraint is applied in advance and cannot be modified later. In other words, indenting on a system-by-system basis is not possible with this mechanism.
Users who need a differently indented system can arrange for that system to start a new score, as demonstrated in the snippet Coda ahead of a line of its own. That may be a reasonable workaround at a more or less natural break such as a coda, but in general the structure clash introduces severe fragmentation requiring compensatory splicing and fudging; see this discussion of problems with this approach.
This snippet demonstrates a pseudo-indent workaround that superimposes an additional, system-wise indentation on the score's unmodified layout indentation, avoiding the abovementioned score change problems. It allows to change the left and right (pseudo) indentation independently; optionally, the instrument name can be changed, too.
Some caveats still apply, though.
- The indenting mechanism is based on an arcane misuse of the
LeftEdgegrob (see Issue #3761) and may therefore become temporarily or permanently broken by side effects of future development changes in the LilyPond code base. - The code is not protected against multiple calls per system break.
\version "2.24"
%%%%%%%% HEADER %%%%%%%%
%
% This code was prompted by
%
% https://lists.gnu.org/archive/html/lilypond-user/2019-07/msg00139.html
%
% and offers a pseudo-indent hack suitable for general use.
%
% keywords:
%
% indent short-indent indentation system line
% mid-score temporarily arbitrary individual single just only once
% coda margin
%
% Put the code before the examples into a file `pseudo-indent.ily` that
% you can include later on in your code with
%
% \include "pseudo-indent.ily"
%%%%%%%% PSEUDO-INDENT FUNCTIONS START %%%%%%%%
% Two functions are provided for indenting individual systems.
%
% - To left-indent a system, apply `\pseudoIndent` before the music
% continues.
% - `\pseudoIndents` does the same but lets you also indent on the
% right.
%
% Both commands accept an optional argument for changing the affected
% system's instrument name(s).
%
% The following conditions must be met to make the functions work.
%
% - It is assumed that the score is not using a ragged-right layout
% (which is the default for multi-system scores).
% - A manual `\break` must be used to call the functions at the start
% of a system.
% - The functions misbehave if called more than once at the same
% system start.
%
% The syntax forms are as follows.
%
% \pseudoIndent [<name-tweaks>] <left-indent>
% \pseudoIndent [<name-tweaks>] <left-indent> <right-indent>
%
% <name-tweaks>
% An optional argument holding a markup list for instrument names
% as an ordered list. If an element is `*`, the previous
% instrument name stays unchanged.
% <left-indent>
% Additional left indentation, in staff-space units. This value
% can be negative with the restriction that the sum
%
% <left-indent> + `short-indent`
%
% must be larger or equal to zero to avoid unsupported
% stretching.
% <right-indent>
% Additional right indentation, in staff-space units; can be
% negative.
%
% Examples:
%
% \pseudoIndent 11
% \pseudoIndents 11 22
% \pseudoIndents \markuplist { "foo" * \myMarkup } 11 22
pseudoIndents =
#(define-music-function (name-tweaks left-indent right-indent)
((markup-list? '()) number? number?)
(define (warn-stretched p1 p2)
(ly:input-warning
(*location*)
(G_
" \\pseudoIndents ~s ~s is stretching staff; expect distorted layout")
p1 p2))
(let*
((narrowing (+ left-indent right-indent))
(set-staffsymbol!
(lambda (staffsymbol-grob) ; change staff to new width
(let*
((left-bound (ly:spanner-bound staffsymbol-grob LEFT))
(left-moment (ly:grob-property left-bound 'when))
;; in first system of score?
(capo? (moment<=? left-moment ZERO-MOMENT))
(layout (ly:grob-layout staffsymbol-grob))
;; debugging info
(lw (ly:output-def-lookup layout 'line-width))
(indent (ly:output-def-lookup
layout
(if capo? 'indent 'short-indent)))
(old-stil (ly:staff-symbol::print staffsymbol-grob))
(staffsymbol-x-ext (ly:stencil-extent old-stil X))
;; For LilyPond versions >=2.19.16 the first system has
;; `old-stil` already narrowed. Compensate for this
;; (i.e., being not pristine) when calculating.
;;
;; - old leftmost-x (its value is needed when setting
;; so-called 'width)
;; - the new width and position (via local variable
;; `narrowing_`)
(ss-t (ly:staff-symbol-line-thickness
staffsymbol-grob))
;; would expect half
(pristine? (<= 0 (car staffsymbol-x-ext) ss-t))
(leftmost-x (+ indent (if pristine? 0 narrowing)))
;; uses 0 if already narrowed
(narrowing_ (if pristine? narrowing 0))
(old-width (+ (interval-length staffsymbol-x-ext)
ss-t))
(new-width (- old-width narrowing_))
;; and set! this immediately
(new-rightmost-x (+ leftmost-x new-width))
(junk (ly:grob-set-property! staffsymbol-grob
'width new-rightmost-x))
(in-situ-stil (ly:staff-symbol::print
staffsymbol-grob))
(new-stil (ly:stencil-translate-axis in-situ-stil
narrowing_
X))
;; for debugging
;; (new-stil (stencil-with-color new-stil red))
(new-x-ext (ly:stencil-extent new-stil X)))
(ly:grob-set-property! staffsymbol-grob 'stencil
new-stil)
(ly:grob-set-property! staffsymbol-grob 'X-extent
new-x-ext))))
;; move grob across to line start
(set-X-offset!
(lambda (margin-grob)
(let* ((old (ly:grob-property-data margin-grob 'X-offset))
(new (lambda (grob)
(+ (if (procedure? old) (old grob) old)
narrowing))))
(ly:grob-set-property! margin-grob 'X-offset new))))
;; tweak both instrument name texts
(tweak-text!
(lambda (i-name-grob mkup)
(when (and (markup? mkup)
(not (string=? (markup->string mkup) "*")))
(ly:grob-set-property! i-name-grob 'long-text mkup)
(ly:grob-set-property! i-name-grob 'text mkup))))
;; on staves, + adapt left margin
(install-narrowing
(lambda (leftedge-grob)
(let*
((sys (ly:grob-system leftedge-grob))
(all-grobs (ly:grob-array->list
(ly:grob-object sys 'all-elements)))
(grobs-named
(lambda (name)
(filter (lambda (x)
(eq? name (grob::name x))) all-grobs)))
(first-leftedge-grob (list-ref
(grobs-named 'LeftEdge) 0))
(relsys-x-of (lambda (g)
(ly:grob-relative-coordinate g sys X)))
(leftedge-x (relsys-x-of first-leftedge-grob))
(leftedged? (lambda (g)
(= (relsys-x-of g) leftedge-x)))
(leftedged-ss (filter leftedged?
(grobs-named 'StaffSymbol))))
;; ignore other left-edges
(when (eq? leftedge-grob first-leftedge-grob)
(for-each set-staffsymbol! leftedged-ss)
(for-each set-X-offset! (grobs-named 'SystemStartBar))
(for-each set-X-offset! (grobs-named 'InstrumentName))
(for-each tweak-text! (grobs-named 'InstrumentName)
name-tweaks))))))
(when (negative? narrowing)
(warn-stretched left-indent right-indent))
;; and continue anyway
#{
% ensure that these overrides are applied only at begin-of-line
% (but this does not protect against unsupported multiple
% application)
\break
% give the spacing engine notice regarding the loss of width
% for music
\once \override Score.LeftEdge.X-extent =
#(cons narrowing narrowing)
% discard line start region of staff and reassemble left-margin
% elements
\once \override Score.LeftEdge.after-line-breaking =
#install-narrowing
% shift the system to partition the narrowing between left and
% right
\overrideProperty Score.NonMusicalPaperColumn
.line-break-system-details
.X-offset #(- right-indent)
% prevent a leftmost bar number entering a stretched staff
\once \override Score.BarNumber.horizon-padding =
#(max 1 (- 1 narrowing))
#}))
pseudoIndent =
#(define-music-function (name-tweaks left-indent)
((markup-list? '()) number?)
#{
\pseudoIndents $name-tweaks $left-indent 0
#})
%%%%%%%% PSEUDO-INDENT FUNCTIONS END %%%%%%%%
%%%%%%%% SETUP FOR WIKI EXAMPLES %%%%%%%%
\paper {
indent = 55\mm
short-indent = 40\mm
system-system-spacing.basic-distance = 10\mm
tagline = ##f
}
m = { f'4 f'4 f'4 f'4 } % one measure
showInfo =
#(define-music-function (info) (markup?)
#{
\once \override Score.LeftEdge.break-visibility =
#begin-of-line-visible
\once \override Score.LeftEdge.stencil =
#(lambda (grob) (grob-interpret-markup grob info))
#})
%%%%%%%% BASIC EXAMPLE %%%%%%%%
% trace short indentation
basic-info-line = \markup \translate #'(0 . -0.2)
\with-color #blue
\draw-dashed-line #'(0 . -49)
v-gap = \markup \vspace #2.25
basic-info-text = \markup \translate #'(-24 . 8.8) \column {
\italic "(indent)"
\v-gap
\italic "(short-indent)"
\v-gap
"\pseudoIndent 22"
\v-gap
"\pseudoIndent 44"
\v-gap
"\pseudoIndents 22 22"
\v-gap
\line { "\pseudoIndents 0 44"
\translate #'(53 . 0)
\italic "with ragged-right ##f (default)" }
\v-gap
\line { "\pseudoIndents 0 20"
\translate #'(72 . 0)
\italic \right-column { "ragged-last ##f" "(default)" } }
}
basic = {
\omit Score.BarNumber
\m \m \m \m \m \m
\break % a short-indented system for reference (also info anchor:)
\showInfo \markup \combine \basic-info-text \basic-info-line
\m \m \m \m \m \m \m
\pseudoIndent 22
\m \m \m \m \m
\pseudoIndent 44
\m \m \m
\pseudoIndents 22 22
\m \m \m
\pseudoIndents 0 44
\m \m \m
\pseudoIndents 0 20
\m \m \m \m \m \bar "|."
}
\markup \huge \bold \column { " " " Basic usage" " "}
\score {
\new Staff { \basic }
\layout {}
}
%%%%%%%% DEMO EXAMPLE %%%%%%%%
% trace right margin
demo-info-line = \markup \translate #'(85.3 . 20.3)
\with-color #blue
\draw-dashed-line #'(0 . -56)
demo-info-text = \markup \translate #'(-24 . 17.3) \column \italic {
"right-indent at head"
" "
"stop/startStaff OK"
" "
" "
" "
" "
"(short-indent)"
" "
" "
" "
" "
"outdent without stretching"
" "
"name-tweaks displaying"
"how its list is ordered"
" "
" "
\line { "using name-tweaks "
\column \upright { " " "\"\"" "\\mkCoda" * }
" to "
\column { " " "hide name"
"change name" "(leave name as it is)" } }
}
% See https://wiki.lilypond.community/wiki/Coda_ahead_of_a_line_of_its_own
mkCoda = \markup \vcenter {
\bold "coda" \fontsize #3 \musicglyph "scripts.coda" }
demo = {
\pseudoIndents 0 22 % no additional left-indenting applied
s1*4
\break % a short-indented system for reference (also info anchor:)
\showInfo \markup \combine \demo-info-text \demo-info-line
s1*7
% display the instrument name ordering
\pseudoIndents \markuplist { 1 2 3 } 11 -4
s1*6
% hide 1; leave 2; change 3
\pseudoIndent \markuplist { "" * \mkCoda } 55
s1*2 \bar "|."
}
demoHi = {
\m \m \m \m
\m \m \m \m \m \m \m
\m \m \m \m \m \m
\m \m
}
demoLo = {
\m \m \stopStaff s1 \startStaff \m
\m \m \m \m \m \m \m
\m \m \m \m \m \m
\m \m
}
\markup \huge \bold \column { " " " Further possibilities" " " }
\score {
\new StaffGroup \with {
instrumentName = "SYS"
shortInstrumentName = "sys" }
<<
\new Staff \with {
instrumentName = "HI"
shortInstrumentName = "hi" }
<< \demo \demoHi >>
\new RhythmicStaff \with {
instrumentName = "LO"
shortInstrumentName = "lo" }
{ \demoLo }
>>
\layout {
\override Score.InstrumentName.self-alignment-X = #RIGHT
\override Score.InstrumentName.padding = 2
}
}
%%%%%%%% END %%%%%%%%