Jump to content

Indenting individual systems: Difference between revisions

From LilyPond wiki
Import snippet from LSR
 
mNo edit summary
 
(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
LilyPond scores are indented rather like text paragraphs: all lines except the first are indented by the same amount (<code>short-indent</code>). This layout constraint is applied in advance and cannot be modified later e.g. on a line-by-line basis.
LilyPond scores are indented similar to text paragraphs: all systems except the first one are indented by the same amount (the value of the <code>short-indent</code> 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.


LilyPond users who need a differently indented line can arrange for that line to start a new score, as in [http://lsr.di.unimi.it/LSR/Item?id=966 LSR966]. 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 [https://lists.gnu.org/archive/html/lilypond-user/2014-12/msg00436.html discussion] re LSR966.
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 [https://lists.gnu.org/archive/html/lilypond-user/2014-12/msg00436.html this discussion] of problems with this approach.


This snippet demonstrates the pseudoIndent workaround which superimposes an additional line-wise indentation on the score's unmodified layout indentation. This approach avoids all those score change problems.
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.


There are independent parameters for left and right indentations. It also comes with a freebie parameter for succinct instrument-name modding.
Some caveats still apply, though.


<br />
* The indenting mechanism is based on an [https://gitlab.com/lilypond/lilypond/-/issues/3761 arcane misuse] of the <code>LeftEdge</code> grob and may therefore become temporarily or permanently broken by side effects of future development changes in the LilyPond code base.
Caveats:
* The code is not protected against multiple calls per system break.


* The indenting mechanism is based on an arcane [https://gitlab.com/lilypond/lilypond/-/issues/3761 misuse] of LeftEdge and may therefore become temporarily or permanently broken through side-effects of developments in the Lilypond code base.
<lilypond version="2.24" full>
* The code is not protected against multiple calls per system break
%%%%%%%% 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 %%%%%%%%


<lilypond version="2.24.0" full>
% Two functions are provided for indenting individual systems.
%%%%%%%% HEADER %%%%%%%%
%
% - 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:
%
%
% this code was prompted by
%   \pseudoIndent 11
% https://lists.gnu.org/archive/html/lilypond-user/2019-07/msg00139.html
%   \pseudoIndents 11 22
% and offers a pseudoIndent hack suitable for general use
%   \pseudoIndents \markuplist { "foo" * \myMarkup } 11 22
 


% keywords:
pseudoIndents =
% indent short-indent indentation system line
#(define-music-function (name-tweaks left-indent right-indent)
% mid-score temporarily arbitrary individual single just only once
  ((markup-list? '()) number? number?)
% coda margin
% mouse's tale acrostic mesostic spine


%%%%%%%% PSEUDOINDENT FUNCTIONS %%%%%%%%
  (define (warn-stretched p1 p2)
    (ly:input-warning
      (*location*)
      (G_
      " \\pseudoIndents ~s ~s is stretching staff; expect distorted layout")
      p1 p2))


% these two functions are for indenting individual systems
  (let*
% - to left-indent a system, apply \pseudoIndent before the music continues
      ((narrowing (+ left-indent right-indent))
% - \pseudoIndents is similar, but lets you also indent on the right
        (set-staffsymbol!
% - both provide an option for changing that system's instrument names
        (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))))


% N.B. these functions
        ;; move grob across to line start
% - assume application to non-ragged lines (generally the default)
        (set-X-offset!
% - include a manual \break to ensure application at line start
        (lambda (margin-grob)
% - misbehave if called more than once at the same line start
          (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))))


% the parameters of the (full) pseudoIndents function are:
        ;; tweak both instrument name texts
% 1: name-tweaks
        (tweak-text!
%      usually omitted; accepts replacement \markup for instrument names
        (lambda (i-name-grob mkup)
%      as an ordered list; starred elements leave their i-names unchanged.
          (when (and (markup? mkup)
% 2: left-indent
                      (not (string=? (markup->string mkup) "*")))
%      additional left-indentation, in staff-space units; can be negative,
            (ly:grob-set-property! i-name-grob 'long-text mkup)
%      but avoid a total indentation which implies (unsupported) stretching.
            (ly:grob-set-property! i-name-grob 'text mkup))))
% 3: right-indent
%      amount of right-indentation, in staff-space units; can be negative.
%      - not offered by the (reduced) pseudoIndent function


        ;; 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))))))


pseudoIndents = % inline alternative to a new \score, also with right-indent
    (when (negative? narrowing)
#(define-music-function (name-tweaks left-indent right-indent)
      (warn-stretched left-indent right-indent))
  ((markup-list? '()) number? number?)
    ;; and continue anyway
  (define (warn-stretched p1 p2) (ly:input-warning (*location*) (G_
    #{
    " pseudoIndents ~s ~s is stretching staff; expect distorted layout") p1 p2))
      % ensure that these overrides are applied only at begin-of-line
  (let* (
      % (but this does not protect against unsupported multiple
    (narrowing (+ left-indent right-indent)) ; of staff implied by args
      % application)
   
      \break
    (set-staffsymbol! (lambda (staffsymbol-grob) ; change staff to new width
      % give the spacing engine notice regarding the loss of width
      (let* (
      % for music
        (left-bound (ly:spanner-bound staffsymbol-grob LEFT))
      \once \override Score.LeftEdge.X-extent =
        (left-moment (ly:grob-property left-bound 'when))
        #(cons narrowing narrowing)
        (capo? (moment<=? left-moment ZERO-MOMENT)) ; in first system of score
      % discard line start region of staff and reassemble left-margin
        (layout (ly:grob-layout staffsymbol-grob))
      % elements
        (lw (ly:output-def-lookup layout 'line-width)) ; debugging info
      \once \override Score.LeftEdge.after-line-breaking =
        (indent (ly:output-def-lookup layout (if capo? 'indent 'short-indent)))
        #install-narrowing
        (old-stil (ly:staff-symbol::print staffsymbol-grob))
      % shift the system to partition the narrowing between left and
        (staffsymbol-x-ext (ly:stencil-extent old-stil X))
      % right
        ;; >=2.19.16's first system has old-stil already narrowed [2]
      \overrideProperty Score.NonMusicalPaperColumn
        ;; compensate for this (ie being not pristine) when calculating
                        .line-break-system-details
        ;; - old leftmost-x (its value is needed when setting so-called 'width)
                        .X-offset #(- right-indent)
        ;; - the new width and position (via local variable narrowing_)
      % prevent a leftmost bar number entering a stretched staff
        (ss-t (ly:staff-symbol-line-thickness staffsymbol-grob))
      \once \override Score.BarNumber.horizon-padding =
        (pristine? (<= 0 (car staffsymbol-x-ext) ss-t)) ; would expect half
        #(max 1 (- 1 narrowing))
        (leftmost-x (+ indent (if pristine? 0 narrowing)))  
    #}))
        (narrowing_ (if pristine? narrowing 0)) ; uses 0 if already narrowed
        (old-width (+ (interval-length staffsymbol-x-ext) ss-t))
        (new-width (- old-width narrowing_))
        (new-rightmost-x (+ leftmost-x new-width)) ; and set! this immediately
        (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))
      ;(new-stil (stencil-with-color new-stil red)) ; for when debugging
        (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)
      )))
   
    (set-X-offset! (lambda (margin-grob) ; move grob across to line start
      (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-text! (lambda (i-name-grob mkup) ; tweak both instrumentname texts 
      (if (and (markup? mkup) (not (string=? (markup->string mkup) "*")))
      (begin
        (ly:grob-set-property! i-name-grob 'long-text mkup)
        (ly:grob-set-property! i-name-grob 'text mkup)
        )))) ; else retain existing text
   
    (install-narrowing (lambda (leftedge-grob) ; on staves, + adapt left margin
      (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))))
      (if (eq? leftedge-grob first-leftedge-grob) ; ignore other leftedges [1]       
      (begin
        (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)
      ))))))
   
  (if (negative? narrowing) (warn-stretched left-indent right-indent))
  #{ % and continue anyway
    % ensure that these overrides are applied only at begin-of-line  
    \break % (but this does not exclude unsupported multiple application)  
    % 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 barnumber entering a stretched staff  
    \once \override Score.BarNumber.horizon-padding = #(max 1 (- 1 narrowing))
  #}))                                      


pseudoIndent = % for changing just left-indent
pseudoIndent =
#(define-music-function (name-tweaks left-indent)  
#(define-music-function (name-tweaks left-indent)
  ((markup-list? '()) number?)  
  ((markup-list? '()) number?)
  #{  
  #{
    \pseudoIndents $name-tweaks $left-indent 0  
    \pseudoIndents $name-tweaks $left-indent 0
  #})
  #})
%%%%%%%% PSEUDO-INDENT FUNCTIONS END %%%%%%%%


% [1] versions <2.19.1 can have end-of-line leftedges too 
%    - these were eliminated in issue 3761
% [2] versions >=2.19.16: the first system behaves differently from the rest
%    - a side effect of issue 660 ?
% [3] versions >=2.23.0: LeftEdge's position may well differ in Y (but not in X)
%    - a side effect of issue 6084 ?


%%%%%%%% LSR EXAMPLES %%%%%%%%
%%%%%%%% SETUP FOR WIKI EXAMPLES %%%%%%%%


\paper {
\paper {
   indent = 55
   indent = 55\mm
   short-indent = 40
   short-indent = 40\mm
   system-system-spacing.basic-distance = 10
   system-system-spacing.basic-distance = 10\mm
   tagline = ##f
   tagline = ##f
}
}
Line 160: Line 234:
m = { f'4 f'4 f'4 f'4 } % one measure
m = { f'4 f'4 f'4 f'4 } % one measure


showInfo = #(define-music-function (info) (markup?)  
showInfo =
#{
#(define-music-function (info) (markup?)
  \once \override Score.LeftEdge.break-visibility = #begin-of-line-visible % [1]
  #{
  \once \override Score.LeftEdge.stencil = #(lambda (grob)  
    \once \override Score.LeftEdge.break-visibility =
  (grob-interpret-markup grob info))  
      #begin-of-line-visible
#})
    \once \override Score.LeftEdge.stencil =
      #(lambda (grob) (grob-interpret-markup grob info))
  #})
 


%%%%%%%% BASIC EXAMPLE %%%%%%%%
%%%%%%%% BASIC EXAMPLE %%%%%%%%


basic-info-line = \markup \translate #'(0 . -0.2) % trace short indentation
% trace short indentation
\with-color #blue \draw-dashed-line #'(0 . -49)  
basic-info-line = \markup \translate #'(0 . -0.2)
                            \with-color #blue
                            \draw-dashed-line #'(0 . -49)


v-gap = \markup \vspace #2.25
v-gap = \markup \vspace #2.25


basic-info-text = \markup \translate #'(-24 . 8.8)
basic-info-text = \markup \translate #'(-24 . 8.8) \column {
\column {
   \italic "(indent)"
   \italic "(indent)"  
   \v-gap
   \v-gap  
   \italic "(short-indent)"
   \italic "(short-indent)"  
   \v-gap
   \v-gap  
   "\pseudoIndent 22"
   "\pseudoIndent 22"  
   \v-gap
   \v-gap  
   "\pseudoIndent 44"
   "\pseudoIndent 44"  
   \v-gap
   \v-gap  
   "\pseudoIndents 22 22"
   "\pseudoIndents 22 22"  
   \v-gap
   \v-gap  
   \line { "\pseudoIndents 0 44"
   \line { "\pseudoIndents 0 44" \translate #'(53 . 0) \italic  
          \translate #'(53 . 0)
  "with  ragged-right  ##f      (default)" }
            \italic "with  ragged-right  ##f      (default)" }
   \v-gap  
   \v-gap
   \line { "\pseudoIndents 0 20" \translate #'(72 . 0) \italic  
   \line { "\pseudoIndents 0 20"
\right-column { "ragged-last  ##f" "(default)" }}}
          \translate #'(72 . 0)
            \italic \right-column { "ragged-last  ##f" "(default)" } }
}


basic = {
basic = {
   \omit Score.BarNumber
   \omit Score.BarNumber
   \m \m \m \m \m \m  
   \m \m \m \m \m \m
   \break % a short-indented system for reference    (also info anchor:)
   \break % a short-indented system for reference    (also info anchor:)
   \showInfo \markup \combine \basic-info-text \basic-info-line
   \showInfo \markup \combine \basic-info-text \basic-info-line
   \m \m \m \m \m \m \m
   \m \m \m \m \m \m \m
   \pseudoIndent 22  
   \pseudoIndent 22
   \m \m \m \m \m  
   \m \m \m \m \m
   \pseudoIndent 44
   \pseudoIndent 44
   \m \m \m
   \m \m \m
   \pseudoIndents 22 22
   \pseudoIndents 22 22
   \m \m \m  
   \m \m \m
   \pseudoIndents 0 44
   \pseudoIndents 0 44
   \m \m \m  
   \m \m \m
   \pseudoIndents 0 20
   \pseudoIndents 0 20
   \m \m \m \m \m \bar "|."
   \m \m \m \m \m \bar "|."
Line 214: Line 295:
\score {
\score {
   \new Staff { \basic }
   \new Staff { \basic }
 
 
   \layout {}
   \layout {}
}
}


%%%%%%%% DEMO EXAMPLE %%%%%%%%
%%%%%%%% DEMO EXAMPLE %%%%%%%%


demo-info-line = \markup\translate #'(85.3 . 20.3) % trace right margin
% trace right margin
\with-color #blue \draw-dashed-line #'(0 . -56)  
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)
demo-info-text = \markup \translate #'(-24 . 17.3) \column \italic {
\column \italic {
   "right-indent at head"
   "right-indent at head"  
   " "
   " "  
   "stop/startStaff OK"
   "stop/startStaff OK"  
   " "
   " "  
   " "
   " "  
   " "
   " "  
   " "
   " "  
   "(short-indent)"
   "(short-indent)"
   " "  
   " "
   " "  
   " "
   " "  
   " "
   " "  
   " "
   "outdent without stretching"  
   "outdent without stretching"
   " "  
   " "
   "name-tweaks displaying"  
   "name-tweaks displaying"
   "how its list is ordered"  
   "how its list is ordered"
   " "  
   " "
   " "  
   " "
   \line {"using name-tweaks  "
   \line { "using name-tweaks  "
    \column \upright { " "  "\"\""  "\\mkCoda"  * }
          \column \upright { " "  "\"\""  "\\mkCoda"  * }
    " to    "
          " to    "
  \column{ " " "hide name" "change name"  "(leave name as it is)"}}
          \column { " " "hide name"
                    "change name"  "(leave name as it is)" } }
}
}


mkCoda = \markup % cf LSR966
% See https://wiki.lilypond.community/wiki/Coda_ahead_of_a_line_of_its_own
\vcenter { \bold "coda" \fontsize #3 \musicglyph "scripts.coda" }
mkCoda = \markup \vcenter {
  \bold "coda" \fontsize #3 \musicglyph "scripts.coda" }


demo = {
demo = {
   \pseudoIndents 0 22 % no additional left-indenting applied
   \pseudoIndents 0 22 % no additional left-indenting applied
   s1*4
   s1*4
   \break % a short-indented system for reference    (also info anchor:)
   \break % a short-indented system for reference    (also info anchor:)
   \showInfo \markup \combine \demo-info-text \demo-info-line
   \showInfo \markup \combine \demo-info-text \demo-info-line
   s1*7  
   s1*7
   \pseudoIndents \markuplist { 1 2 3 } 11 -4 % display the i-name ordering
  % display the instrument name ordering
   s1*6  
   \pseudoIndents \markuplist { 1 2 3 } 11 -4
   \pseudoIndent \markuplist { "" * \mkCoda } 55 % hide 1; leave 2; change 3
   s1*6
  % hide 1; leave 2; change 3
   \pseudoIndent \markuplist { "" * \mkCoda } 55
   s1*2 \bar "|."
   s1*2 \bar "|."
}
}


demoHi = {
demoHi = {
   \m \m \m \m
   \m \m \m \m
   \m \m \m \m \m \m \m  
   \m \m \m \m \m \m \m
   \m \m \m \m \m \m  
   \m \m \m \m \m \m
   \m \m  
   \m \m
}
}


demoLo = {
demoLo = {
   \m \m \stopStaff s1 \startStaff \m
   \m \m \stopStaff s1 \startStaff \m
   \m \m \m \m \m \m \m
   \m \m \m \m \m \m \m
   \m \m \m \m \m \m  
   \m \m \m \m \m \m
   \m \m  
   \m \m
}
}




\markup \huge \bold \column { " "  "      Further possibilities"  " "}
\markup \huge \bold \column { " "  "      Further possibilities"  " " }


\score {
\score {
   \new StaffGroup
   \new StaffGroup \with {
  \with { instrumentName = "SYS" shortInstrumentName = "sys" }
    instrumentName = "SYS"
   <<
    shortInstrumentName = "sys" }
     \new Staff  
   <<
    \with { instrumentName = "HI" shortInstrumentName = "hi" }
     \new Staff \with {
  << \demo \demoHi >>
      instrumentName = "HI"
     \new RhythmicStaff
      shortInstrumentName = "hi" }
    \with { instrumentName = "LO" shortInstrumentName = "lo" }
    << \demo \demoHi >>
     \new RhythmicStaff \with {
      instrumentName = "LO"
      shortInstrumentName = "lo" }
     { \demoLo }
     { \demoLo }
   >>
   >>
 
 
   \layout {
   \layout {
     \override Score.InstrumentName.self-alignment-X = #RIGHT
     \override Score.InstrumentName.self-alignment-X = #RIGHT
Line 302: Line 392:
</lilypond>
</lilypond>


[[Category:Breaks]]
[[Category:Scheme]]
[[Category:Staff notation]]
[[Category:Tweaks and overrides]]
[[Category:Workaround]]
[[Category:Workaround]]
[[Category:Snippet]]

Latest revision as of 06:06, 15 December 2025

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 LeftEdge grob 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 %%%%%%%%